diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java | 339 |
1 files changed, 124 insertions, 215 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index ab00db13a..043c40b25 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -23,26 +23,20 @@ import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; import android.database.Cursor; -import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; import android.support.v4.util.LongSparseArray; -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.S2K; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; +import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.WrappedSignature; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; @@ -56,14 +50,12 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.security.SignatureException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; public class ProviderHelper { @@ -141,47 +133,29 @@ public class ProviderHelper { public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types) throws NotFoundException { - return getGenericData(KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types); + return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - /** - * Find the master key id related to a given query. The id will either be extracted from the - * query, which should work for all specific /key_rings/ queries, or will be queried if it can't. - */ - public long extractOrGetMasterKeyId(Uri queryUri) - throws NotFoundException { - // try extracting from the uri first - String firstSegment = queryUri.getPathSegments().get(1); - if (!firstSegment.equals("find")) try { - return Long.parseLong(firstSegment); - } catch (NumberFormatException e) { - // didn't work? oh well. - Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying..."); - } - return getMasterKeyId(queryUri); - } - - public long getMasterKeyId(Uri queryUri) throws NotFoundException { - Object data = getGenericData(queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER); - if (data != null) { - return (Long) data; - } else { - throw new NotFoundException(); - } - } - - public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) { + private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) { Cursor cursor = mContentResolver.query(queryUri, new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, null, null, null); - LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount()); + LongSparseArray<UncachedPublicKey> result = + new LongSparseArray<UncachedPublicKey>(cursor.getCount()); try { if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); byte[] data = cursor.getBlob(1); if (data != null) { - result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data)); + try { + result.put(masterKeyId, + UncachedKeyRing.decodePublicFromData(data).getPublicKey()); + } catch(PgpGeneralException e) { + Log.e(Constants.TAG, "Error parsing keyring, skipping."); + } catch(IOException e) { + Log.e(Constants.TAG, "IO error, skipping keyring"); + } } } while (cursor.moveToNext()); } finally { @@ -193,57 +167,74 @@ public class ProviderHelper { return result; } - public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException { - LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri); - if (result.size() == 0) { - throw new NotFoundException("PGPKeyRing object not found!"); - } else { - return result.valueAt(0); - } + public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) { + return new CachedPublicKeyRing(this, queryUri); } - public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId) - throws NotFoundException { - Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)); - long masterKeyId = getMasterKeyId(uri); - return getPGPPublicKeyRing(masterKeyId); + public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException { + return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false); } - public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId) - throws NotFoundException { - Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)); - long masterKeyId = getMasterKeyId(uri); - return getPGPSecretKeyRing(masterKeyId); + public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException { + return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false); } - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId - */ - public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException { - Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); - return (PGPPublicKeyRing) getPGPKeyRing(queryUri); + public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException { + return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true); } - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId - */ - public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException { - Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - return (PGPSecretKeyRing) getPGPKeyRing(queryUri); + public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException { + return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true); + } + + private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException { + Cursor cursor = mContentResolver.query(queryUri, + new String[]{ + // we pick from cache only information that is not easily available from keyrings + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED, + // and of course, ring data + secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA + }, null, null, null + ); + try { + if (cursor != null && cursor.moveToFirst()) { + + boolean hasAnySecret = cursor.getInt(0) > 0; + int verified = cursor.getInt(1); + byte[] blob = cursor.getBlob(2); + if(secret &! hasAnySecret) { + throw new NotFoundException("Secret key not available!"); + } + return secret + ? new WrappedSecretKeyRing(blob, hasAnySecret, verified) + : new WrappedPublicKeyRing(blob, hasAnySecret, verified); + } else { + throw new NotFoundException("Key not found!"); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } } /** * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException { - PGPPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyID(); + public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException { + if (keyRing.isSecret()) { + throw new RuntimeException("Tried to save secret keyring as public! " + + "This is a bug, please file a bug report."); + } + + UncachedPublicKey masterKey = keyRing.getPublicKey(); + long masterKeyId = masterKey.getKeyId(); // IF there is a secret key, preserve it! - PGPSecretKeyRing secretRing = null; + UncachedKeyRing secretRing = null; try { - secretRing = getPGPSecretKeyRing(masterKeyId); + secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); } catch (NotFoundException e) { Log.e(Constants.TAG, "key not found!"); } @@ -266,36 +257,38 @@ public class ProviderHelper { ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); int rank = 0; - for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) { + for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) { operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); ++rank; } // get a list of owned secret keys, for verification filtering - LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri()); + LongSparseArray<UncachedPublicKey> allKeyRings = + getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); // special case: available secret keys verify themselves! - if (secretRing != null) - allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing); + if (secretRing != null) { + allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); + } // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. List<UserIdItem> uids = new ArrayList<UserIdItem>(); - for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { + for (String userId : new IterableIterator<String>( + masterKey.getUnorderedUserIds().iterator())) { UserIdItem item = new UserIdItem(); uids.add(item); item.userId = userId; // look through signatures for this specific key - for (PGPSignature cert : new IterableIterator<PGPSignature>( - masterKey.getSignaturesForID(userId))) { - long certId = cert.getKeyID(); + for (WrappedSignature cert : new IterableIterator<WrappedSignature>( + masterKey.getSignaturesForId(userId))) { + long certId = cert.getKeyId(); try { // self signature if (certId == masterKeyId) { - cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey); - if (!cert.verifyCertification(userId, masterKey)) { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userId)) { // not verified?! dang! TODO notify user? this is kinda serious... Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); continue; @@ -304,31 +297,22 @@ public class ProviderHelper { if (item.selfCert == null || item.selfCert.getCreationTime().before(cert.getCreationTime())) { item.selfCert = cert; - item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID(); - item.isRevoked = - cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION; + item.isPrimary = cert.isPrimaryUserId(); + item.isRevoked = cert.isRevocation(); } } // verify signatures from known private keys if (allKeyRings.indexOfKey(certId) >= 0) { - // mark them as verified - cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME), - allKeyRings.get(certId).getPublicKey()); - if (cert.verifyCertification(userId, masterKey)) { + cert.init(allKeyRings.get(certId)); + if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); } } - } catch (SignatureException e) { + } catch (PgpGeneralException e) { Log.e(Constants.TAG, "Signature verification failed! " - + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId()) + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); - } catch (PGPException e) { - Log.e(Constants.TAG, "Signature verification failed! " - + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) - + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e); } } } @@ -365,7 +349,7 @@ public class ProviderHelper { // Save the saved keyring (if any) if (secretRing != null) { - saveKeyRing(secretRing); + saveSecretKeyRing(secretRing); } } @@ -374,8 +358,8 @@ public class ProviderHelper { String userId; boolean isPrimary = false; boolean isRevoked = false; - PGPSignature selfCert; - List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>(); + WrappedSignature selfCert; + List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>(); @Override public int compareTo(UserIdItem o) { @@ -395,8 +379,13 @@ 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(PGPSecretKeyRing keyRing) throws IOException { - long masterKeyId = keyRing.getPublicKey().getKeyID(); + public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException { + if (!keyRing.isSecret()) { + throw new RuntimeException("Tried to save publkc keyring as secret! " + + "This is a bug, please file a bug report."); + } + + long masterKeyId = keyRing.getMasterKeyId(); { Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); @@ -408,14 +397,10 @@ public class ProviderHelper { values.put(Keys.HAS_SECRET, 1); // then, mark exactly the keys we have available - for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { - S2K s2k = sub.getS2K(); - // Set to 1, except if the encryption type is GNU_DUMMY_S2K - if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { - mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{ - Long.toString(sub.getKeyID()) - }); - } + for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) { + mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] { + Long.toString(sub) + }); } // this implicitly leaves all keys which were not in the secret key ring // with has_secret = 0 @@ -436,39 +421,39 @@ public class ProviderHelper { /** * Saves (or updates) a pair of public and secret KeyRings in the database */ - public void saveKeyRing(PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException { - long masterKeyId = pubRing.getPublicKey().getKeyID(); + public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { + long masterKeyId = pubRing.getPublicKey().getKeyId(); - // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below) + // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below) mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); // save public keyring - saveKeyRing(pubRing); - saveKeyRing(privRing); + savePublicKeyRing(pubRing); + saveSecretKeyRing(secRing); } /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ private ContentProviderOperation - buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException { + buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException { ContentValues values = new ContentValues(); values.put(Keys.MASTER_KEY_ID, masterKeyId); values.put(Keys.RANK, rank); - values.put(Keys.KEY_ID, key.getKeyID()); + values.put(Keys.KEY_ID, key.getKeyId()); values.put(Keys.KEY_SIZE, key.getBitStrength()); values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.FINGERPRINT, key.getFingerprint()); - values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key))); - values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key))); - values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.isRevoked()); + values.put(Keys.CAN_CERTIFY, key.canCertify()); + values.put(Keys.CAN_SIGN, key.canSign()); + values.put(Keys.CAN_ENCRYPT, key.canEncrypt()); + values.put(Keys.IS_REVOKED, key.maybeRevoked()); - values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PgpKeyHelper.getExpiryDate(key); + values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000); + Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); } @@ -482,11 +467,12 @@ public class ProviderHelper { * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ private ContentProviderOperation - buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException { + buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified) + throws IOException { ContentValues values = new ContentValues(); values.put(Certs.MASTER_KEY_ID, masterKeyId); values.put(Certs.RANK, rank); - values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID()); + values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId()); values.put(Certs.TYPE, cert.getSignatureType()); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); values.put(Certs.VERIFIED, verified); @@ -514,23 +500,11 @@ public class ProviderHelper { return ContentProviderOperation.newInsert(uri).withValues(values).build(); } - private String getKeyRingAsArmoredString(byte[] data) throws IOException { - Object keyRing = null; - if (data != null) { - keyRing = PgpConversionHelper.BytesToPGPKeyRing(data); - } + private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException { + UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = new ArmoredOutputStream(bos); - aos.setHeader("Version", PgpHelper.getFullVersion(mContext)); - - if (keyRing instanceof PGPSecretKeyRing) { - aos.write(((PGPSecretKeyRing) keyRing).getEncoded()); - } else if (keyRing instanceof PGPPublicKeyRing) { - aos.write(((PGPPublicKeyRing) keyRing).getEncoded()); - } - aos.close(); - + keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext)); String armoredKey = bos.toString("UTF-8"); Log.d(Constants.TAG, "armoredKey:" + armoredKey); @@ -539,77 +513,12 @@ public class ProviderHelper { } public String getKeyRingAsArmoredString(Uri uri) - throws NotFoundException, IOException { + throws NotFoundException, IOException, PgpGeneralException { byte[] data = (byte[]) getGenericData( uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB); return getKeyRingAsArmoredString(data); } - /** - * TODO: currently not used, but will be needed to upload many keys at once! - * - * @param masterKeyIds - * @return - * @throws IOException - */ - public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds) - throws IOException { - ArrayList<String> output = new ArrayList<String>(); - - if (masterKeyIds == null || masterKeyIds.length == 0) { - Log.e(Constants.TAG, "No master keys given!"); - return output; - } - - // Build a cursor for the selected masterKeyIds - Cursor cursor; - { - String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN ("; - for (int i = 0; i < masterKeyIds.length; ++i) { - if (i != 0) { - inMasterKeyList += ", "; - } - inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]); - } - inMasterKeyList += ")"; - - cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{ - KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA - }, inMasterKeyList, null, null); - } - - try { - if (cursor != null) { - int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID); - int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA); - if (cursor.moveToFirst()) { - do { - Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); - - byte[] data = cursor.getBlob(dataCol); - - // get actual keyring data blob and write it to ByteArrayOutputStream - try { - output.add(getKeyRingAsArmoredString(data)); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException", e); - } - } while (cursor.moveToNext()); - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - if (output.size() > 0) { - return output; - } else { - return null; - } - } - public ArrayList<String> getRegisteredApiApps() { Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null); |