wrapped-key-ring: more refactoring - no more pgp imports in KeychainIntentService!

This commit is contained in:
Vincent Breitmoser 2014-05-03 22:29:09 +02:00
parent 32baf42515
commit f524fa692c
14 changed files with 212 additions and 272 deletions

View File

@ -1,44 +1,26 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPSignature;
public abstract class CachedKeyRing { public abstract class CachedKeyRing {
private final long mMasterKeyId; private final long mMasterKeyId;
private final int mKeySize;
private final boolean mIsRevoked;
private final boolean mCanCertify; private final boolean mCanCertify;
private final long mCreation;
private final long mExpiry;
private final int mAlgorithm;
private final byte[] mFingerprint; private final byte[] mFingerprint;
private final String mUserId; private final String mUserId;
private final int mVerified; private final int mVerified;
private final boolean mHasSecret; private final boolean mHasSecret;
protected CachedKeyRing(long masterKeyId, int keySize, boolean isRevoked, protected CachedKeyRing(long masterKeyId, boolean canCertify,
boolean canCertify, long creation, long expiry, int algorithm,
byte[] fingerprint, String userId, int verified, boolean hasSecret) byte[] fingerprint, String userId, int verified, boolean hasSecret)
{ {
mMasterKeyId = masterKeyId; mMasterKeyId = masterKeyId;
mKeySize = keySize;
mIsRevoked = isRevoked;
mCanCertify = canCertify; mCanCertify = canCertify;
mCreation = creation;
mExpiry = expiry;
mAlgorithm = algorithm;
mFingerprint = fingerprint; mFingerprint = fingerprint;
mUserId = userId; mUserId = userId;
mVerified = verified; mVerified = verified;
mHasSecret = hasSecret; mHasSecret = hasSecret;
} }
public boolean isRevoked() {
return mIsRevoked;
}
public byte[] getFingerprint() { public byte[] getFingerprint() {
return mFingerprint; return mFingerprint;
} }
@ -54,4 +36,12 @@ public abstract class CachedKeyRing {
return mVerified; return mVerified;
} }
public boolean canCertify() {
return mCanCertify;
}
public boolean hasSecret() {
return mHasSecret;
}
} }

View File

@ -31,6 +31,10 @@ public class CachedPublicKey {
return mKey.getKeyID(); return mKey.getKeyID();
} }
public boolean isRevoked() {
return mKey.isRevoked();
}
public Date getCreationTime() { public Date getCreationTime() {
return mKey.getCreationTime(); return mKey.getCreationTime();
} }
@ -48,6 +52,15 @@ public class CachedPublicKey {
return calendar.getTime(); 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() { public boolean isMasterKey() {
return mKey.isMasterKey(); return mKey.isMasterKey();
} }

View File

@ -2,7 +2,6 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
@ -17,7 +16,6 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException; import java.io.IOException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
public class CachedPublicKeyRing extends CachedKeyRing { public class CachedPublicKeyRing extends CachedKeyRing {
@ -30,8 +28,7 @@ public class CachedPublicKeyRing extends CachedKeyRing {
byte[] fingerprint, String userId, int verified, boolean hasSecret, byte[] fingerprint, String userId, int verified, boolean hasSecret,
byte[] pubkey) byte[] pubkey)
{ {
super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry, super(masterKeyId, canCertify, fingerprint, userId, verified, hasSecret);
algorithm, fingerprint, userId, verified, hasSecret);
mPubKey = pubkey; mPubKey = pubkey;
} }
@ -55,24 +52,46 @@ public class CachedPublicKeyRing extends CachedKeyRing {
return new CachedPublicKey(this, getRing().getPublicKey(id)); return new CachedPublicKey(this, getRing().getPublicKey(id));
} }
public CachedPublicKey getFirstEncryptSubkey() throws PgpGeneralException { public CachedPublicKey getFirstSignSubkey() throws PgpGeneralException {
// only return master key if no other encryption key is available // only return master key if no other signing key is available
PGPPublicKey masterKey = null; CachedPublicKey masterKey = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) { for (PGPPublicKey k : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if (key.isRevoked() || !isEncryptionKey(key) || isExpired(key)) { CachedPublicKey key = new CachedPublicKey(this, k);
if (key.isRevoked() || key.canSign() || key.isExpired()) {
continue; continue;
} }
if (key.isMasterKey()) { if (key.isMasterKey()) {
masterKey = key; masterKey = key;
} else { } else {
return new CachedPublicKey(this, key); return key;
} }
} }
if(masterKey == null) { if(masterKey == null) {
// TODO proper exception // TODO proper exception
throw new PgpGeneralException("key not found"); throw new PgpGeneralException("key not found");
} }
return new CachedPublicKey(this, masterKey); return masterKey;
}
public CachedPublicKey getFirstEncryptSubkey() throws PgpGeneralException {
// only return master key if no other encryption key is available
CachedPublicKey masterKey = null;
for (PGPPublicKey k : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
CachedPublicKey key = new CachedPublicKey(this, k);
if (key.isRevoked() || key.canEncrypt() || key.isExpired()) {
continue;
}
if (key.isMasterKey()) {
masterKey = key;
} else {
return key;
}
}
if(masterKey == null) {
// TODO proper exception
throw new PgpGeneralException("key not found");
}
return masterKey;
} }
public boolean verifySubkeyBinding(CachedPublicKey cachedSubkey) { public boolean verifySubkeyBinding(CachedPublicKey cachedSubkey) {
@ -132,55 +151,6 @@ public class CachedPublicKeyRing extends CachedKeyRing {
} }
static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
}
if (key.getVersion() <= 3) {
// this must be true now
return key.isEncryptionKey();
}
// special cases
if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null
&& (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true;
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null
&& (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true;
}
}
return false;
}
static boolean isExpired(PGPPublicKey key) {
Date creationDate = key.getCreationTime();
Date expiryDate = key.getValidSeconds() > 0
? new Date(creationDate.getTime() + key.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts, static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey, PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) { PGPPublicKey signingPublicKey) {

View File

@ -9,9 +9,14 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.util.Iterator; import java.util.Iterator;
public class CachedSecretKeyRing extends CachedKeyRing { public class CachedSecretKeyRing extends CachedKeyRing {
@ -23,12 +28,15 @@ public class CachedSecretKeyRing extends CachedKeyRing {
byte[] fingerprint, String userId, int verified, boolean hasSecret, byte[] fingerprint, String userId, int verified, boolean hasSecret,
byte[] blob) byte[] blob)
{ {
super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry, super(masterKeyId, canCertify, fingerprint, userId, verified, hasSecret);
algorithm, fingerprint, userId, verified, hasSecret);
mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob); mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob);
} }
PGPSecretKeyRing getRing() {
return mRing;
}
public CachedSecretKey getSubKey() { public CachedSecretKey getSubKey() {
return new CachedSecretKey(this, mRing.getSecretKey()); return new CachedSecretKey(this, mRing.getSecretKey());
} }
@ -110,4 +118,27 @@ public class CachedSecretKeyRing extends CachedKeyRing {
return false; return false;
} }
public UncachedSecretKeyRing changeSecretKeyPassphrase(String oldPassphrase,
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
mRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return new UncachedSecretKeyRing(newKeyRing);
}
} }

View File

@ -165,36 +165,4 @@ public class PgpConversionHelper {
return os.toByteArray(); return os.toByteArray();
} }
/**
* Convert from PGPSecretKey to byte[]
*
* @param key
* @return
*/
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
try {
return key.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
}
/**
* Convert from PGPSecretKeyRing to byte[]
*
* @param keyRing
* @return
*/
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
try {
return keyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
}
} }

View File

@ -277,7 +277,7 @@ public class PgpImportExport {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public int storeKeyRingInCache(UncachedKeyRing keyring) { public int storeKeyRingInCache(UncachedKeyRing keyring) {
int status = RETURN_ERROR; int status;
try { try {
PGPSecretKeyRing secretKeyRing = keyring.getSecretRing(); PGPSecretKeyRing secretKeyRing = keyring.getSecretRing();
PGPPublicKeyRing publicKeyRing = keyring.getPublicRing(); PGPPublicKeyRing publicKeyRing = keyring.getPublicRing();

View File

@ -81,18 +81,6 @@ public class PgpKeyHelper {
return getExpiryDate(key.getPublicKey()); return getExpiryDate(key.getPublicKey());
} }
@Deprecated
public static boolean isExpired(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key);
Date now = new Date();
if (now.compareTo(creationDate) >= 0
&& (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
return false;
}
return true;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Deprecated @Deprecated
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) { public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
@ -110,48 +98,6 @@ public class PgpKeyHelper {
return null; return null;
} }
@SuppressWarnings("unchecked")
@Deprecated
private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (isSigningKey(key)) {
signingKeys.add(key);
}
}
return signingKeys;
}
@Deprecated
private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
PGPSecretKey masterKey = null;
for (int i = 0; i < signingKeys.size(); ++i) {
PGPSecretKey key = signingKeys.get(i);
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
@Deprecated
public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
if (signingKeys.size() == 0) {
return null;
}
return signingKeys.get(0);
}
public static int getKeyUsage(PGPSecretKey key) { public static int getKeyUsage(PGPSecretKey key) {
return getKeyUsage(key.getPublicKey()); return getKeyUsage(key.getPublicKey());
} }

View File

@ -35,7 +35,6 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
@ -48,10 +47,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.Primes;
import java.io.IOException; import java.io.IOException;
@ -66,7 +63,6 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@ -124,7 +120,7 @@ public class PgpKeyOperation {
*/ */
// TODO: key flags? // TODO: key flags?
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey) boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException { PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@ -188,35 +184,15 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc) PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), try {
sha1Calc, isMasterKey, keyEncryptor); return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor).getEncoded();
} catch(IOException e) {
throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
} }
public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase, public UncachedKeyRing buildNewSecretKey(
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
keyRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return newKeyRing;
}
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
SaveKeyringParcel saveParcel) SaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
@ -357,17 +333,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing); return new UncachedKeyRing(publicKeyRing, secretKeyRing);
} }
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR, public UncachedKeyRing buildSecretKey(CachedSecretKeyRing wmKR,
PGPPublicKeyRing pKR, CachedPublicKeyRing wpKR,
SaveKeyringParcel saveParcel) SaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
PGPSecretKeyRing mKR = wmKR.getRing();
PGPPublicKeyRing pKR = wpKR.getRing();
updateProgress(R.string.progress_building_key, 0, 100); updateProgress(R.string.progress_building_key, 0, 100);
PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) { if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = ""; saveParcel.oldPassphrase = "";
@ -404,7 +382,7 @@ public class PgpKeyOperation {
} }
} }
masterKey = mKR.getSecretKey(); PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0); int usageId = saveParcel.keysUsages.get(0);
@ -686,7 +664,7 @@ public class PgpKeyOperation {
*/ */
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR); return new UncachedKeyRing(pKR, mKR);
} }

View File

@ -1,7 +1,15 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class UncachedKeyRing { public class UncachedKeyRing {
@ -9,8 +17,6 @@ public class UncachedKeyRing {
final PGPSecretKeyRing mSecretRing; final PGPSecretKeyRing mSecretRing;
UncachedKeyRing(PGPPublicKeyRing publicRing, PGPSecretKeyRing secretRing) { UncachedKeyRing(PGPPublicKeyRing publicRing, PGPSecretKeyRing secretRing) {
// this one must not be false!
assert(publicRing != null);
mPublicRing = publicRing; mPublicRing = publicRing;
mSecretRing = secretRing; mSecretRing = secretRing;
} }
@ -26,4 +32,29 @@ public class UncachedKeyRing {
public PGPSecretKeyRing getSecretRing() { public PGPSecretKeyRing getSecretRing() {
return mSecretRing; return mSecretRing;
} }
public byte[] getFingerprint() {
return mPublicRing.getPublicKey().getFingerprint();
}
public static UncachedKeyRing decodePubkeyFromData(byte[] data)
throws PgpGeneralException, IOException {
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(data));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPPublicKeyRing) {
return new UncachedKeyRing((PGPPublicKeyRing) obj);
} else {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
}
} else {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
}
}
} }

View File

@ -0,0 +1,19 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPSecretKeyRing;
public class UncachedSecretKeyRing {
final PGPSecretKeyRing mSecretRing;
UncachedSecretKeyRing(PGPSecretKeyRing secretRing) {
mSecretRing = secretRing;
}
// Breaking the pattern here, for key import!
// TODO reduce this from public to default visibility!
public PGPSecretKeyRing getSecretKeyRing() {
return mSecretRing;
}
}

View File

@ -43,6 +43,8 @@ import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@ -484,12 +486,22 @@ public class ProviderHelper {
} }
} }
/**
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database!
*/
public void saveKeyRing(UncachedSecretKeyRing wrappedRing) throws IOException {
// TODO split up getters
PGPSecretKeyRing keyRing = wrappedRing.getSecretKeyRing();
saveKeyRing(keyRing);
}
/** /**
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database! * is already in the database!
*/ */
public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException { public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
long masterKeyId = keyRing.getPublicKey().getKeyID(); long masterKeyId = keyRing.getSecretKey().getKeyID();
{ {
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@ -526,6 +538,12 @@ public class ProviderHelper {
} }
public void saveKeyRing(UncachedKeyRing wrappedRing) throws IOException {
PGPPublicKeyRing pubRing = wrappedRing.getPublicRing();
PGPSecretKeyRing secRing = wrappedRing.getSecretRing();
saveKeyRing(pubRing, secRing);
}
/** /**
* Saves (or updates) a pair of public and secret KeyRings in the database * Saves (or updates) a pair of public and secret KeyRings in the database
*/ */

View File

@ -27,12 +27,6 @@ import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.FileHelper;
@ -41,7 +35,6 @@ import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CachedSecretKey; import org.sufficientlysecure.keychain.pgp.CachedSecretKey;
import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
@ -51,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -63,7 +57,6 @@ import org.sufficientlysecure.keychain.util.KeychainServiceListener;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@ -516,19 +509,19 @@ public class KeychainIntentService extends IntentService
/* Operation */ /* Operation */
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
if (!canSign) { if (!canSign) {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100)); setProgress(R.string.progress_building_key, 0, 100);
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRing(masterKeyId); CachedSecretKeyRing keyRing = providerHelper.getCachedSecretKeyRing(masterKeyId);
keyRing = keyOperations.changeSecretKeyPassphrase(keyRing, UncachedSecretKeyRing newKeyRing =
oldPassphrase, newPassphrase); keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase);
setProgress(R.string.progress_saving_key_ring, 50, 100); setProgress(R.string.progress_saving_key_ring, 50, 100);
providerHelper.saveKeyRing(keyRing); providerHelper.saveKeyRing(newKeyRing);
setProgress(R.string.progress_done, 100, 100); setProgress(R.string.progress_done, 100, 100);
} else { } else {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair; UncachedKeyRing pair;
try { try {
PGPSecretKeyRing privkey = providerHelper.getPGPSecretKeyRing(masterKeyId); CachedSecretKeyRing privkey = providerHelper.getCachedSecretKeyRing(masterKeyId);
PGPPublicKeyRing pubkey = providerHelper.getPGPPublicKeyRing(masterKeyId); CachedPublicKeyRing pubkey = providerHelper.getCachedPublicKeyRing(masterKeyId);
pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
@ -537,7 +530,7 @@ public class KeychainIntentService extends IntentService
setProgress(R.string.progress_saving_key_ring, 90, 100); setProgress(R.string.progress_saving_key_ring, 90, 100);
// save the pair // save the pair
providerHelper.saveKeyRing(pair.second, pair.first); providerHelper.saveKeyRing(pair);
setProgress(R.string.progress_done, 100, 100); setProgress(R.string.progress_done, 100, 100);
} }
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase); PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
@ -557,13 +550,11 @@ public class KeychainIntentService extends IntentService
/* Operation */ /* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize, byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
passphrase, masterKey);
/* Output */ /* Output */
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY, resultData.putByteArray(RESULT_NEW_KEY, newKey);
PgpConversionHelper.PGPSecretKeyToBytes(newKey));
OtherHelper.logDebugBundle(resultData, "resultData"); OtherHelper.logDebugBundle(resultData, "resultData");
@ -576,7 +567,6 @@ public class KeychainIntentService extends IntentService
try { try {
/* Input */ /* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
ArrayList<Integer> keyUsageList = new ArrayList<Integer>(); ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */ /* Operation */
@ -589,23 +579,27 @@ public class KeychainIntentService extends IntentService
keysTotal); keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Constants.choice.algorithm.rsa, ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buf;
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true); 4096, passphrase, true);
newKeys.add(masterKey); os.write(buf);
keyUsageList.add(KeyFlags.CERTIFY_OTHER); keyUsageList.add(KeyFlags.CERTIFY_OTHER);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
newKeys.add(subKey); os.write(buf);
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE); keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
subKey = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
newKeys.add(subKey); os.write(buf);
keyUsageList.add(KeyFlags.SIGN_DATA); keyUsageList.add(KeyFlags.SIGN_DATA);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
@ -614,10 +608,8 @@ public class KeychainIntentService extends IntentService
// for sign // for sign
/* Output */ /* Output */
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY, resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray());
PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList); resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData"); OtherHelper.logDebugBundle(resultData, "resultData");
@ -755,30 +747,13 @@ public class KeychainIntentService extends IntentService
} }
// create PGPKeyRing object based on downloaded armored key // create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null; UncachedKeyRing downloadedKey =
BufferedInputStream bufferedInput = UncachedKeyRing.decodePubkeyFromData(downloadedKeyBytes);
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null) {
Log.d(Constants.TAG, "Found class: " + obj.getClass());
if (obj instanceof PGPKeyRing) {
downloadedKey = (PGPKeyRing) obj;
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
}
// verify downloaded key by comparing fingerprints // verify downloaded key by comparing fingerprints
if (entry.getFingerPrintHex() != null) { if (entry.getFingerPrintHex() != null) {
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex( String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
downloadedKey.getPublicKey().getFingerprint()); downloadedKey.getFingerprint());
if (downloadedKeyFp.equals(entry.getFingerPrintHex())) { if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " + Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
"the requested fingerprint!"); "the requested fingerprint!");
@ -790,7 +765,7 @@ public class KeychainIntentService extends IntentService
// save key bytes in entry object for doing the // save key bytes in entry object for doing the
// actual import afterwards // actual import afterwards
entry.setBytes(downloadedKey.getEncoded()); entry.setBytes(downloadedKeyBytes);
} }
Intent importIntent = new Intent(this, KeychainIntentService.class); Intent importIntent = new Intent(this, KeychainIntentService.class);

View File

@ -34,7 +34,12 @@ import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.CachedPublicKey;
import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CachedSecretKey;
import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -144,18 +149,14 @@ public class EncryptAsymmetricFragment extends Fragment {
*/ */
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds, private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
ProviderHelper providerHelper) { ProviderHelper providerHelper) {
// TODO all of this works under the assumption that the first suitable subkey is always used!
// not sure if we need to distinguish between different subkeys here?
if (preselectedSignatureKeyId != 0) { if (preselectedSignatureKeyId != 0) {
// TODO: don't use bouncy castle objects!
try { try {
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRingWithKeyId( CachedPublicKeyRing keyring =
preselectedSignatureKeyId); providerHelper.getCachedPublicKeyRing(preselectedSignatureKeyId);
if(keyring.hasSecret()) {
PGPSecretKey masterKey = keyRing.getSecretKey(); setSignatureKeyId(keyring.getMasterKeyId());
if (masterKey != null) {
PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
if (signKey != null) {
setSignatureKeyId(masterKey.getKeyID());
}
} }
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
@ -165,7 +166,6 @@ public class EncryptAsymmetricFragment extends Fragment {
if (preselectedEncryptionKeyIds != null) { if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>(); Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
// TODO One query per selected key?! wtf
try { try {
long id = providerHelper.getMasterKeyId( long id = providerHelper.getMasterKeyId(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(

View File

@ -500,5 +500,6 @@
<string name="title_view_cert">View Certificate Details</string> <string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string> <string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string> <string name="can_sign_not">cannot sign</string>
<string name="error_encoding">Encoding error</string>
</resources> </resources>