Fix layout, add API notes to README, register fingerprint URIs, some reformatting (sry)

This commit is contained in:
Dominik Schürmann 2014-02-01 16:17:33 +01:00
parent 4c8809042f
commit 5359205b50
19 changed files with 650 additions and 674 deletions

View File

@ -266,6 +266,20 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden" > android:windowSoftInputMode="stateHidden" >
<!-- Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<!-- Android's scheme matcher is case-sensitive, so include most likely variations -->
<data android:scheme="openpgp4fpr" />
<data android:scheme="OPENPGP4FPR" />
<data android:scheme="OpenPGP4FPR" />
<data android:scheme="OpenPGP4Fpr" />
<data android:scheme="OpenPGP4fpr" />
</intent-filter>
<!-- Handle NFC tags detected from outside our application --> <!-- Handle NFC tags detected from outside our application -->
<intent-filter> <intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" /> <action android:name="android.nfc.action.NDEF_DISCOVERED" />

View File

@ -42,17 +42,6 @@ import android.content.Context;
public class PgpKeyHelper { public class PgpKeyHelper {
/**
* Returns the last 9 chars of a fingerprint
*
* @param fingerprint
* String containing short or long fingerprint
* @return
*/
public static String shortifyFingerprint(String fingerprint) {
return fingerprint.substring(41);
}
public static Date getCreationDate(PGPPublicKey key) { public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime(); return key.getCreationTime();
} }
@ -175,10 +164,6 @@ public class PgpKeyHelper {
return true; return true;
} }
public static boolean isExpired(PGPSecretKey key) {
return isExpired(key.getPublicKey());
}
public static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) { public static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>(); Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing); Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
@ -460,12 +445,12 @@ public class PgpKeyHelper {
* @param fp * @param fp
* @return * @return
*/ */
public static String convertFingerprintToHex(byte[] fp) { public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
String fingerPrint = ""; String fingerPrint = "";
for (int i = 0; i < fp.length; ++i) { for (int i = 0; i < fp.length; ++i) {
if (i != 0 && i % 10 == 0) { if (chunked && i != 0 && i % 10 == 0) {
fingerPrint += " "; fingerPrint += " ";
} else if (i != 0 && i % 2 == 0) { } else if (chunked && i != 0 && i % 2 == 0) {
fingerPrint += " "; fingerPrint += " ";
} }
String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US); String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
@ -491,21 +476,21 @@ public class PgpKeyHelper {
key = secretKey.getPublicKey(); key = secretKey.getPublicKey();
} }
return convertFingerprintToHex(key.getFingerprint()); return convertFingerprintToHex(key.getFingerprint(), true);
} }
public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) { public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
return secretKey.isPrivateKeyEmpty(); return secretKey.isPrivateKeyEmpty();
} }
public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) { // public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); // PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
if (secretKey == null) { // if (secretKey == null) {
Log.e(Constants.TAG, "Key could not be found!"); // Log.e(Constants.TAG, "Key could not be found!");
return false; // could be a public key, assume it is not empty // return false; // could be a public key, assume it is not empty
} // }
return isSecretKeyPrivateEmpty(secretKey); // return isSecretKeyPrivateEmpty(secretKey);
} // }
public static String convertKeyIdToHex(long keyId) { public static String convertKeyIdToHex(long keyId) {
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);

View File

@ -45,6 +45,7 @@ public class KeychainContract {
String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob
String RANK = "rank"; String RANK = "rank";
String FINGERPRINT = "fingerprint";
} }
interface UserIdsColumns { interface UserIdsColumns {

View File

@ -31,7 +31,7 @@ import android.provider.BaseColumns;
public class KeychainDatabase extends SQLiteOpenHelper { public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "apg.db"; private static final String DATABASE_NAME = "apg.db";
private static final int DATABASE_VERSION = 6; private static final int DATABASE_VERSION = 7;
public interface Tables { public interface Tables {
String KEY_RINGS = "key_rings"; String KEY_RINGS = "key_rings";
@ -42,33 +42,45 @@ public class KeychainDatabase extends SQLiteOpenHelper {
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, " + KeyRingsColumns.MASTER_KEY_ID + " INT64, "
+ KeyRingsColumns.TYPE + " INTEGER, "
+ KeyRingsColumns.KEY_RING_DATA + " BLOB)"; + KeyRingsColumns.KEY_RING_DATA + " BLOB)";
private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY + KeysColumns.KEY_ID + " INT64, "
+ " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE + KeysColumns.TYPE + " INTEGER, "
+ " INTEGER, " + KeysColumns.CAN_CERTIFY + " INTEGER, " + KeysColumns.CAN_SIGN + KeysColumns.IS_MASTER_KEY + " INTEGER, "
+ " INTEGER, " + KeysColumns.CAN_ENCRYPT + " INTEGER, " + KeysColumns.IS_REVOKED + KeysColumns.ALGORITHM + " INTEGER, "
+ " INTEGER, " + KeysColumns.CREATION + " INTEGER, " + KeysColumns.EXPIRY + KeysColumns.KEY_SIZE + " INTEGER, "
+ " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," + KeysColumns.RANK + " INTEGER, " + KeysColumns.CAN_CERTIFY + " INTEGER, "
+ KeysColumns.CAN_SIGN + " INTEGER, "
+ KeysColumns.CAN_ENCRYPT + " INTEGER, "
+ KeysColumns.IS_REVOKED + " INTEGER, "
+ KeysColumns.CREATION + " INTEGER, "
+ KeysColumns.EXPIRY + " INTEGER, "
+ KeysColumns.KEY_DATA + " BLOB,"
+ KeysColumns.RANK + " INTEGER, "
+ KeysColumns.FINGERPRINT + " BLOB, "
+ KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
+ KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
+ BaseColumns._ID + ") ON DELETE CASCADE)"; + BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " + UserIdsColumns.USER_ID + " TEXT, "
+ UserIdsColumns.RANK + " INTEGER, "
+ UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
+ UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
+ BaseColumns._ID + ") ON DELETE CASCADE)"; + BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + ApiAppsColumns.PACKAGE_SIGNATURE + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
+ " BLOB, " + ApiAppsColumns.KEY_ID + " INT64, " + ApiAppsColumns.ENCRYPTION_ALGORITHM + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
+ " INTEGER, " + ApiAppsColumns.HASH_ALORITHM + " INTEGER, " + ApiAppsColumns.KEY_ID + " INT64, "
+ ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
+ ApiAppsColumns.COMPRESSION + " INTEGER)"; + ApiAppsColumns.COMPRESSION + " INTEGER)";
KeychainDatabase(Context context) { KeychainDatabase(Context context) {
@ -103,21 +115,24 @@ public class KeychainDatabase extends SQLiteOpenHelper {
Log.w(Constants.TAG, "Upgrading database to version " + version); Log.w(Constants.TAG, "Upgrading database to version " + version);
switch (version) { switch (version) {
case 3: case 3:
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY
+ " INTEGER DEFAULT 0;"); + " INTEGER DEFAULT 0;");
db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY
+ " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;"); + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
break; break;
case 4: case 4:
db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS);
case 5: case 5:
// new column: package_signature // new column: package_signature
db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS); db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS);
case 6:
default: // new column: fingerprint
break; db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
+ " BLOB;");
default:
break;
} }
} }

View File

@ -240,7 +240,9 @@ public class KeychainProvider extends ContentProvider {
private KeychainDatabase mApgDatabase; private KeychainDatabase mApgDatabase;
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public boolean onCreate() { public boolean onCreate() {
mUriMatcher = buildUriMatcher(); mUriMatcher = buildUriMatcher();
@ -248,52 +250,54 @@ public class KeychainProvider extends ContentProvider {
return true; return true;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
final int match = mUriMatcher.match(uri); final int match = mUriMatcher.match(uri);
switch (match) { switch (match) {
case PUBLIC_KEY_RING: case PUBLIC_KEY_RING:
case PUBLIC_KEY_RING_BY_EMAILS: case PUBLIC_KEY_RING_BY_EMAILS:
case PUBLIC_KEY_RING_BY_LIKE_EMAIL: case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
case SECRET_KEY_RING: case SECRET_KEY_RING:
case SECRET_KEY_RING_BY_EMAILS: case SECRET_KEY_RING_BY_EMAILS:
case SECRET_KEY_RING_BY_LIKE_EMAIL: case SECRET_KEY_RING_BY_LIKE_EMAIL:
return KeyRings.CONTENT_TYPE; return KeyRings.CONTENT_TYPE;
case PUBLIC_KEY_RING_BY_ROW_ID: case PUBLIC_KEY_RING_BY_ROW_ID:
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case PUBLIC_KEY_RING_BY_KEY_ID: case PUBLIC_KEY_RING_BY_KEY_ID:
case SECRET_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_KEY_ID: case SECRET_KEY_RING_BY_KEY_ID:
return KeyRings.CONTENT_ITEM_TYPE; return KeyRings.CONTENT_ITEM_TYPE;
case PUBLIC_KEY_RING_KEY: case PUBLIC_KEY_RING_KEY:
case SECRET_KEY_RING_KEY: case SECRET_KEY_RING_KEY:
return Keys.CONTENT_TYPE; return Keys.CONTENT_TYPE;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID:
return Keys.CONTENT_ITEM_TYPE; return Keys.CONTENT_ITEM_TYPE;
case PUBLIC_KEY_RING_USER_ID: case PUBLIC_KEY_RING_USER_ID:
case SECRET_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID:
return UserIds.CONTENT_TYPE; return UserIds.CONTENT_TYPE;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
return UserIds.CONTENT_ITEM_TYPE; return UserIds.CONTENT_ITEM_TYPE;
case API_APPS: case API_APPS:
return ApiApps.CONTENT_TYPE; return ApiApps.CONTENT_TYPE;
case API_APPS_BY_ROW_ID: case API_APPS_BY_ROW_ID:
case API_APPS_BY_PACKAGE_NAME: case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE; return ApiApps.CONTENT_ITEM_TYPE;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
} }
@ -306,36 +310,36 @@ public class KeychainProvider extends ContentProvider {
private int getKeyType(int match) { private int getKeyType(int match) {
int type; int type;
switch (match) { switch (match) {
case PUBLIC_KEY_RING: case PUBLIC_KEY_RING:
case PUBLIC_KEY_RING_BY_ROW_ID: case PUBLIC_KEY_RING_BY_ROW_ID:
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case PUBLIC_KEY_RING_BY_KEY_ID: case PUBLIC_KEY_RING_BY_KEY_ID:
case PUBLIC_KEY_RING_BY_EMAILS: case PUBLIC_KEY_RING_BY_EMAILS:
case PUBLIC_KEY_RING_BY_LIKE_EMAIL: case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
case PUBLIC_KEY_RING_KEY: case PUBLIC_KEY_RING_KEY:
case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case PUBLIC_KEY_RING_USER_ID: case PUBLIC_KEY_RING_USER_ID:
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
type = KeyTypes.PUBLIC; type = KeyTypes.PUBLIC;
break; break;
case SECRET_KEY_RING: case SECRET_KEY_RING:
case SECRET_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_KEY_ID: case SECRET_KEY_RING_BY_KEY_ID:
case SECRET_KEY_RING_BY_EMAILS: case SECRET_KEY_RING_BY_EMAILS:
case SECRET_KEY_RING_BY_LIKE_EMAIL: case SECRET_KEY_RING_BY_LIKE_EMAIL:
case SECRET_KEY_RING_KEY: case SECRET_KEY_RING_KEY:
case SECRET_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
type = KeyTypes.SECRET; type = KeyTypes.SECRET;
break; break;
default: default:
Log.e(Constants.TAG, "Unknown match " + match); Log.e(Constants.TAG, "Unknown match " + match);
type = -1; type = -1;
break; break;
} }
return type; return type;
@ -350,10 +354,13 @@ public class KeychainProvider extends ContentProvider {
HashMap<String, String> projectionMap = new HashMap<String, String>(); HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
+ KeyRingsColumns.MASTER_KEY_ID);
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
+ KeyRingsColumns.KEY_RING_DATA); + KeyRingsColumns.KEY_RING_DATA);
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID);
// TODO: deprecated master key id
//projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
return projectionMap; return projectionMap;
@ -386,22 +393,20 @@ public class KeychainProvider extends ContentProvider {
} }
/** /**
* Builds default query for keyRings: KeyRings table is joined with UserIds * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
*
* @param qb
* @param match
* @param isMasterKey
* @param sortOrder
* @return
*/ */
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) { private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
// public or secret keyring // public or secret keyring
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
// join keyrings with userIds to every keyring // join keyrings with keys and userIds
qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "(" // Only get user id and key with rank 0 (main user id and main key)
+ Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
+ Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
+ KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "."
+ KeysColumns.RANK + " = '0') " + " INNER JOIN " + Tables.USER_IDS + " ON "
+ "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
+ UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
+ UserIdsColumns.RANK + " = '0')"); + UserIdsColumns.RANK + " = '0')");
@ -411,16 +416,11 @@ public class KeychainProvider extends ContentProvider {
} }
/** /**
* Builds default query for keyRings: KeyRings table is joined with Keys and UserIds * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
* * <p/>
* @param qb * Here only one key should be selected in the query to return a single keyring!
* @param match
* @param isMasterKey
* @param sortOrder
* @return
*/ */
private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match, private SQLiteQueryBuilder buildKeyRingQueryWithSpecificKey(SQLiteQueryBuilder qb, int match) {
String sortOrder) {
// public or secret keyring // public or secret keyring
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
@ -438,11 +438,13 @@ public class KeychainProvider extends ContentProvider {
return qb; return qb;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) { String sortOrder) {
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@ -451,169 +453,169 @@ public class KeychainProvider extends ContentProvider {
int match = mUriMatcher.match(uri); int match = mUriMatcher.match(uri);
switch (match) { switch (match) {
case PUBLIC_KEY_RING: case PUBLIC_KEY_RING:
case SECRET_KEY_RING: case SECRET_KEY_RING:
qb = buildKeyRingQuery(qb, match, sortOrder); qb = buildKeyRingQuery(qb, match);
if (TextUtils.isEmpty(sortOrder)) { if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID:
qb = buildKeyRingQuery(qb, match, sortOrder);
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
qb = buildKeyRingQuery(qb, match, sortOrder);
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case SECRET_KEY_RING_BY_KEY_ID:
case PUBLIC_KEY_RING_BY_KEY_ID:
qb = buildKeyRingQueryWithKeys(qb, match, sortOrder);
qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case SECRET_KEY_RING_BY_EMAILS:
case PUBLIC_KEY_RING_BY_EMAILS:
qb = buildKeyRingQuery(qb, match, sortOrder);
String emails = uri.getLastPathSegment();
String chunks[] = emails.split(" *, *");
boolean gotCondition = false;
String emailWhere = "";
for (int i = 0; i < chunks.length; ++i) {
if (chunks[i].length() == 0) {
continue;
} }
if (i != 0) {
emailWhere += " OR ";
}
emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
// match '*<email>', so it has to be at the *end* of the user id
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
gotCondition = true;
}
if (gotCondition) { break;
case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID:
qb = buildKeyRingQuery(qb, match);
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
qb = buildKeyRingQuery(qb, match);
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case SECRET_KEY_RING_BY_KEY_ID:
case PUBLIC_KEY_RING_BY_KEY_ID:
qb = buildKeyRingQueryWithSpecificKey(qb, match);
qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
}
break;
case SECRET_KEY_RING_BY_EMAILS:
case PUBLIC_KEY_RING_BY_EMAILS:
qb = buildKeyRingQuery(qb, match);
String emails = uri.getLastPathSegment();
String chunks[] = emails.split(" *, *");
boolean gotCondition = false;
String emailWhere = "";
for (int i = 0; i < chunks.length; ++i) {
if (chunks[i].length() == 0) {
continue;
}
if (i != 0) {
emailWhere += " OR ";
}
emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
// match '*<email>', so it has to be at the *end* of the user id
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
gotCondition = true;
}
if (gotCondition) {
qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
+ Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
+ " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere
+ "))");
}
break;
case SECRET_KEY_RING_BY_LIKE_EMAIL:
case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
qb = buildKeyRingQuery(qb, match);
String likeEmail = uri.getLastPathSegment();
String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE "
+ DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
+ Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
+ " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
+ "))"); + "))");
}
break; break;
case SECRET_KEY_RING_BY_LIKE_EMAIL: case PUBLIC_KEY_RING_KEY:
case PUBLIC_KEY_RING_BY_LIKE_EMAIL: case SECRET_KEY_RING_KEY:
qb = buildKeyRingQuery(qb, match, sortOrder); qb.setTables(Tables.KEYS);
qb.appendWhere(KeysColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
String likeEmail = uri.getLastPathSegment(); qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE " qb.setProjectionMap(getProjectionMapForKeys());
+ DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " break;
+ Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
+ " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
+ "))");
break; case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID:
qb.setTables(Tables.KEYS);
qb.appendWhere(KeysColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
case PUBLIC_KEY_RING_KEY: qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
case SECRET_KEY_RING_KEY: qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.setTables(Tables.KEYS);
qb.appendWhere(KeysColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); qb.appendWhere(" AND " + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2)); qb.appendWhereEscapeString(uri.getLastPathSegment());
qb.setProjectionMap(getProjectionMapForKeys()); qb.setProjectionMap(getProjectionMapForKeys());
break; break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_USER_ID:
qb.setTables(Tables.KEYS); qb.setTables(Tables.USER_IDS);
qb.appendWhere(KeysColumns.TYPE + " = "); qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); break;
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.appendWhere(" AND " + BaseColumns._ID + " = "); case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
qb.appendWhereEscapeString(uri.getLastPathSegment()); case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
qb.setTables(Tables.USER_IDS);
qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.setProjectionMap(getProjectionMapForKeys()); qb.appendWhere(" AND " + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break; break;
case PUBLIC_KEY_RING_USER_ID: case API_APPS:
case SECRET_KEY_RING_USER_ID: qb.setTables(Tables.API_APPS);
qb.setTables(Tables.USER_IDS);
qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break; break;
case API_APPS_BY_ROW_ID:
qb.setTables(Tables.API_APPS);
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: qb.appendWhere(BaseColumns._ID + " = ");
case SECRET_KEY_RING_USER_ID_BY_ROW_ID: qb.appendWhereEscapeString(uri.getLastPathSegment());
qb.setTables(Tables.USER_IDS);
qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.appendWhere(" AND " + BaseColumns._ID + " = "); break;
qb.appendWhereEscapeString(uri.getLastPathSegment()); case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break; break;
case API_APPS: default:
qb.setTables(Tables.API_APPS); throw new IllegalArgumentException("Unknown URI " + uri);
break;
case API_APPS_BY_ROW_ID:
qb.setTables(Tables.API_APPS);
qb.appendWhere(BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
} }
@ -634,14 +636,16 @@ public class KeychainProvider extends ContentProvider {
Log.d(Constants.TAG, Log.d(Constants.TAG,
"Query: " "Query: "
+ qb.buildQuery(projection, selection, selectionArgs, null, null, + qb.buildQuery(projection, selection, selectionArgs, null, null,
orderBy, null)); orderBy, null));
Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c)); Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c));
} }
return c; return c;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
@ -654,56 +658,56 @@ public class KeychainProvider extends ContentProvider {
final int match = mUriMatcher.match(uri); final int match = mUriMatcher.match(uri);
switch (match) { switch (match) {
case PUBLIC_KEY_RING: case PUBLIC_KEY_RING:
values.put(KeyRings.TYPE, KeyTypes.PUBLIC); values.put(KeyRings.TYPE, KeyTypes.PUBLIC);
rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_KEY: case PUBLIC_KEY_RING_KEY:
values.put(Keys.TYPE, KeyTypes.PUBLIC); values.put(Keys.TYPE, KeyTypes.PUBLIC);
rowId = db.insertOrThrow(Tables.KEYS, null, values); rowId = db.insertOrThrow(Tables.KEYS, null, values);
rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_USER_ID: case PUBLIC_KEY_RING_USER_ID:
rowId = db.insertOrThrow(Tables.USER_IDS, null, values); rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case SECRET_KEY_RING: case SECRET_KEY_RING:
values.put(KeyRings.TYPE, KeyTypes.SECRET); values.put(KeyRings.TYPE, KeyTypes.SECRET);
rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case SECRET_KEY_RING_KEY: case SECRET_KEY_RING_KEY:
values.put(Keys.TYPE, KeyTypes.SECRET); values.put(Keys.TYPE, KeyTypes.SECRET);
rowId = db.insertOrThrow(Tables.KEYS, null, values); rowId = db.insertOrThrow(Tables.KEYS, null, values);
rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case SECRET_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID:
rowId = db.insertOrThrow(Tables.USER_IDS, null, values); rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
break; break;
case API_APPS: case API_APPS:
rowId = db.insertOrThrow(Tables.API_APPS, null, values); rowId = db.insertOrThrow(Tables.API_APPS, null, values);
rowUri = ApiApps.buildIdUri(Long.toString(rowId)); rowUri = ApiApps.buildIdUri(Long.toString(rowId));
break; break;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
// notify of changes in db // notify of changes in db
@ -716,7 +720,9 @@ public class KeychainProvider extends ContentProvider {
return rowUri; return rowUri;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "delete(uri=" + uri + ")"); Log.v(Constants.TAG, "delete(uri=" + uri + ")");
@ -728,45 +734,45 @@ public class KeychainProvider extends ContentProvider {
String defaultSelection = null; String defaultSelection = null;
switch (match) { switch (match) {
case PUBLIC_KEY_RING_BY_ROW_ID: case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID:
defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
// corresponding keys and userIds are deleted by ON DELETE CASCADE // corresponding keys and userIds are deleted by ON DELETE CASCADE
count = db.delete(Tables.KEY_RINGS, count = db.delete(Tables.KEY_RINGS,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
selectionArgs); selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID:
defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
// corresponding keys and userIds are deleted by ON DELETE CASCADE // corresponding keys and userIds are deleted by ON DELETE CASCADE
count = db.delete(Tables.KEY_RINGS, count = db.delete(Tables.KEY_RINGS,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
selectionArgs); selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID:
count = db.delete(Tables.KEYS, count = db.delete(Tables.KEYS,
buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
selectionArgs); selectionArgs);
break; break;
case API_APPS_BY_ROW_ID: case API_APPS_BY_ROW_ID:
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection), count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
selectionArgs); selectionArgs);
break; break;
case API_APPS_BY_PACKAGE_NAME: case API_APPS_BY_PACKAGE_NAME:
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
selectionArgs); selectionArgs);
break; break;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
// notify of changes in db // notify of changes in db
@ -775,7 +781,9 @@ public class KeychainProvider extends ContentProvider {
return count; return count;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
@ -787,54 +795,54 @@ public class KeychainProvider extends ContentProvider {
try { try {
final int match = mUriMatcher.match(uri); final int match = mUriMatcher.match(uri);
switch (match) { switch (match) {
case PUBLIC_KEY_RING_BY_ROW_ID: case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID:
defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
count = db.update( count = db.update(
Tables.KEY_RINGS, Tables.KEY_RINGS,
values, values,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
selection), selectionArgs); selection), selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID:
defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
count = db.update( count = db.update(
Tables.KEY_RINGS, Tables.KEY_RINGS,
values, values,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
selection), selectionArgs); selection), selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID:
count = db count = db
.update(Tables.KEYS, values, .update(Tables.KEYS, values,
buildDefaultKeysSelection(uri, getKeyType(match), selection), buildDefaultKeysSelection(uri, getKeyType(match), selection),
selectionArgs); selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break; break;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
count = db.update(Tables.USER_IDS, values, count = db.update(Tables.USER_IDS, values,
buildDefaultUserIdsSelection(uri, selection), selectionArgs); buildDefaultUserIdsSelection(uri, selection), selectionArgs);
break; break;
case API_APPS_BY_ROW_ID: case API_APPS_BY_ROW_ID:
count = db.update(Tables.API_APPS, values, count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, false, selection), selectionArgs); buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
break; break;
case API_APPS_BY_PACKAGE_NAME: case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values, count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
break; break;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
// notify of changes in db // notify of changes in db
@ -856,7 +864,7 @@ public class KeychainProvider extends ContentProvider {
* @return * @return
*/ */
private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType,
String selection) { String selection) {
String andType = ""; String andType = "";
if (keyType != null) { if (keyType != null) {
andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType;

View File

@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.UserAttributePacket;
import org.spongycastle.bcpg.UserAttributeSubpacket;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
@ -55,10 +57,6 @@ public class ProviderHelper {
/** /**
* Private helper method to get PGPKeyRing from database * Private helper method to get PGPKeyRing from database
*
* @param context
* @param queryUri
* @return
*/ */
public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri, Cursor cursor = context.getContentResolver().query(queryUri,
@ -83,10 +81,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId
*
* @param context
* @param rowId
* @return
*/ */
public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
@ -95,10 +89,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId
*
* @param context
* @param masterKeyId
* @return
*/ */
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
long masterKeyId) { long masterKeyId) {
@ -109,10 +99,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key
* with this keyId * with this keyId
*
* @param context
* @param keyId
* @return
*/ */
public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) {
Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId));
@ -122,10 +108,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPPublicKey object from the database blob associated with a key with * Retrieves the actual PGPPublicKey object from the database blob associated with a key with
* this keyId * this keyId
*
* @param context
* @param keyId
* @return
*/ */
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
@ -138,10 +120,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId
*
* @param context
* @param rowId
* @return
*/ */
public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
@ -150,10 +128,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*
* @param context
* @param masterKeyId
* @return
*/ */
public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context,
long masterKeyId) { long masterKeyId) {
@ -164,10 +138,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key
* with this keyId * with this keyId
*
* @param context
* @param keyId
* @return
*/ */
public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) {
Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId));
@ -177,10 +147,6 @@ public class ProviderHelper {
/** /**
* Retrieves the actual PGPSecretKey object from the database blob associated with a key with * Retrieves the actual PGPSecretKey object from the database blob associated with a key with
* this keyId * this keyId
*
* @param context
* @param keyId
* @return
*/ */
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
@ -193,12 +159,6 @@ public class ProviderHelper {
/** /**
* Saves PGPPublicKeyRing with its keys and userIds in DB * Saves PGPPublicKeyRing with its keys and userIds in DB
*
* @param context
* @param keyRing
* @return
* @throws IOException
* @throws GeneralException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException { public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException {
@ -263,12 +223,6 @@ public class ProviderHelper {
/** /**
* Saves PGPSecretKeyRing with its keys and userIds in DB * Saves PGPSecretKeyRing with its keys and userIds in DB
*
* @param context
* @param keyRing
* @return
* @throws IOException
* @throws GeneralException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException {
@ -333,13 +287,6 @@ public class ProviderHelper {
/** /**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*
* @param context
* @param keyRingRowId
* @param key
* @param rank
* @return
* @throws IOException
*/ */
private static ContentProviderOperation buildPublicKeyOperations(Context context, private static ContentProviderOperation buildPublicKeyOperations(Context context,
long keyRingRowId, PGPPublicKey key, int rank) throws IOException { long keyRingRowId, PGPPublicKey key, int rank) throws IOException {
@ -367,13 +314,6 @@ public class ProviderHelper {
/** /**
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
*
* @param context
* @param keyRingRowId
* @param key
* @param rank
* @return
* @throws IOException
*/ */
private static ContentProviderOperation buildPublicUserIdOperations(Context context, private static ContentProviderOperation buildPublicUserIdOperations(Context context,
long keyRingRowId, String userId, int rank) { long keyRingRowId, String userId, int rank) {
@ -389,13 +329,6 @@ public class ProviderHelper {
/** /**
* Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing
*
* @param context
* @param keyRingRowId
* @param key
* @param rank
* @return
* @throws IOException
*/ */
private static ContentProviderOperation buildSecretKeyOperations(Context context, private static ContentProviderOperation buildSecretKeyOperations(Context context,
long keyRingRowId, PGPSecretKey key, int rank) throws IOException { long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
@ -432,13 +365,6 @@ public class ProviderHelper {
/** /**
* Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing
*
* @param context
* @param keyRingRowId
* @param key
* @param rank
* @return
* @throws IOException
*/ */
private static ContentProviderOperation buildSecretUserIdOperations(Context context, private static ContentProviderOperation buildSecretUserIdOperations(Context context,
long keyRingRowId, String userId, int rank) { long keyRingRowId, String userId, int rank) {
@ -454,10 +380,6 @@ public class ProviderHelper {
/** /**
* Private helper method * Private helper method
*
* @param context
* @param queryUri
* @return
*/ */
private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) { private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri, Cursor cursor = context.getContentResolver().query(queryUri,
@ -482,9 +404,6 @@ public class ProviderHelper {
/** /**
* Retrieves ids of all SecretKeyRings * Retrieves ids of all SecretKeyRings
*
* @param context
* @return
*/ */
public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) { public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(); Uri queryUri = KeyRings.buildSecretKeyRingsUri();
@ -493,9 +412,6 @@ public class ProviderHelper {
/** /**
* Retrieves ids of all PublicKeyRings * Retrieves ids of all PublicKeyRings
*
* @param context
* @return
*/ */
public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) { public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(); Uri queryUri = KeyRings.buildPublicKeyRingsUri();
@ -514,10 +430,6 @@ public class ProviderHelper {
/** /**
* Get master key id of keyring by its row id * Get master key id of keyring by its row id
*
* @param context
* @param keyRingRowId
* @return
*/ */
public static long getPublicMasterKeyId(Context context, long keyRingRowId) { public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
@ -526,10 +438,6 @@ public class ProviderHelper {
/** /**
* Get empty status of master key of keyring by its row id * Get empty status of master key of keyring by its row id
*
* @param context
* @param keyRingRowId
* @return
*/ */
public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) { public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
@ -538,11 +446,6 @@ public class ProviderHelper {
/** /**
* Private helper method to get master key private empty status of keyring by its row id * Private helper method to get master key private empty status of keyring by its row id
*
* @param context
* @param queryUri
* @param keyRingRowId
* @return
*/ */
private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) { private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) {
String[] projection = new String[]{ String[] projection = new String[]{
@ -572,10 +475,6 @@ public class ProviderHelper {
/** /**
* Get master key id of keyring by its row id * Get master key id of keyring by its row id
*
* @param context
* @param keyRingRowId
* @return
*/ */
public static long getSecretMasterKeyId(Context context, long keyRingRowId) { public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
@ -583,41 +482,84 @@ public class ProviderHelper {
} }
/** /**
* Private helper method to get master key id of keyring by its row id * Get master key id of key
*
* @param context
* @param queryUri
* @param keyRingRowId
* @return
*/ */
public static long getMasterKeyId(Context context, Uri queryUri) { public static long getMasterKeyId(Context context, Uri queryUri) {
String[] projection = new String[]{KeyRings.MASTER_KEY_ID}; String[] projection = new String[]{KeyRings.MASTER_KEY_ID};
Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
ContentResolver cr = context.getContentResolver();
Cursor cursor = cr.query(queryUri, projection, null, null, null);
long masterKeyId = -1; long masterKeyId = -1;
if (cursor != null && cursor.moveToFirst()) { try {
int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); if (cursor != null && cursor.moveToFirst()) {
int masterKeyIdCol = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
masterKeyId = cursor.getLong(masterKeyIdCol); masterKeyId = cursor.getLong(masterKeyIdCol);
} }
} finally {
if (cursor != null) { if (cursor != null) {
cursor.close(); cursor.close();
}
} }
return masterKeyId; return masterKeyId;
} }
public static ArrayList<String> getPublicKeyRingsAsArmoredString(Context context, /**
long[] masterKeyIds) { * Get fingerprint of key
return getKeyRingsAsArmoredString(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds); */
} public static byte[] getFingerprint(Context context, Uri queryUri) {
String[] projection = new String[]{Keys.FINGERPRINT};
Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
public static ArrayList<String> getSecretKeyRingsAsArmoredString(Context context, byte[] fingerprint = null;
long[] masterKeyIds) { try {
return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds); if (cursor != null && cursor.moveToFirst()) {
int col = cursor.getColumnIndexOrThrow(Keys.FINGERPRINT);
fingerprint = cursor.getBlob(col);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
// FALLBACK: If fingerprint is not in database, get it from key blob!
// this could happen if the key was saved by a previous version of Keychain!
if (fingerprint == null) {
Log.d(Constants.TAG, "FALLBACK: fingerprint is not in database, get it from key blob!");
// get master key id
projection = new String[]{KeyRings.MASTER_KEY_ID};
cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
long masterKeyId = 0;
try {
if (cursor != null && cursor.moveToFirst()) {
int col = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
masterKeyId = cursor.getLong(col);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, masterKeyId);
// if it is no public key get it from your own keys...
if (key == null) {
PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, masterKeyId);
if (secretKey == null) {
Log.e(Constants.TAG, "Key could not be found!");
return null;
}
key = secretKey.getPublicKey();
}
fingerprint = key.getFingerprint();
}
return fingerprint;
} }
public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri, public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
@ -681,14 +623,6 @@ public class ProviderHelper {
} }
} }
public static byte[] getPublicKeyRingsAsByteArray(Context context, long[] masterKeyIds) {
return getKeyRingsAsByteArray(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds);
}
public static byte[] getSecretKeyRingsAsByteArray(Context context, long[] masterKeyIds) {
return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
}
public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) { public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();

View File

@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
@ -35,6 +36,7 @@ import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage; import android.nfc.NdefMessage;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.os.Bundle; import android.os.Bundle;
@ -56,6 +58,8 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_QR_CODE"; + "IMPORT_KEY_FROM_QR_CODE";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEYSERVER";
// Actions for internal use only: // Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
@ -63,24 +67,21 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
public static final String ACTION_IMPORT_KEY_FROM_NFC = Constants.INTENT_PREFIX public static final String ACTION_IMPORT_KEY_FROM_NFC = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_NFC"; + "IMPORT_KEY_FROM_NFC";
// only used by IMPORT // only used by ACTION_IMPORT_KEY
public static final String EXTRA_KEY_BYTES = "key_bytes"; public static final String EXTRA_KEY_BYTES = "key_bytes";
// TODO: import keys from server // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER
// public static final String EXTRA_KEY_ID = "keyId"; public static final String EXTRA_QUERY = "query";
public static final String FINGERPRINT_SCHEME = "openpgp4fpr";
protected boolean mDeleteAfterImport = false; protected boolean mDeleteAfterImport = false;
FileDialogFragment mFileDialog; // view
ImportKeysListFragment mListFragment; private ImportKeysListFragment mListFragment;
OnNavigationListener mOnNavigationListener; private String[] mNavigationStrings;
String[] mNavigationStrings; private Fragment mCurrentFragment;
private BootstrapButton mImportButton;
Fragment mCurrentFragment;
BootstrapButton mImportButton;
// BootstrapButton mImportSignUploadButton;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -95,21 +96,11 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
importKeys(); importKeys();
} }
}); });
// mImportSignUploadButton = (BootstrapButton) findViewById(R.id.import_sign_and_upload);
// mImportSignUploadButton.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// signAndUploadOnClick();
// }
// });
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
setupDrawerNavigation(savedInstanceState); setupDrawerNavigation(savedInstanceState);
// set actionbar without home button if called from another app
// ActionBarHelper.setBackButton(this);
// set drop down navigation // set drop down navigation
mNavigationStrings = getResources().getStringArray(R.array.import_action_list); mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
Context context = getSupportActionBar().getThemedContext(); Context context = getSupportActionBar().getThemedContext();
@ -125,6 +116,8 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
protected void handleActions(Bundle savedInstanceState, Intent intent) { protected void handleActions(Bundle savedInstanceState, Intent intent) {
String action = intent.getAction(); String action = intent.getAction();
Bundle extras = intent.getExtras(); Bundle extras = intent.getExtras();
Uri dataUri = intent.getData();
String scheme = intent.getScheme();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
@ -139,6 +132,15 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
action = ACTION_IMPORT_KEY; action = ACTION_IMPORT_KEY;
} }
/**
* Scanning a fingerprint directly with Barcode Scanner
*/
if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(FINGERPRINT_SCHEME)) {
getSupportActionBar().setSelectedNavigationItem(0);
loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[0]);
loadFromFingerprintUri(dataUri);
}
/** /**
* Keychain's own Actions * Keychain's own Actions
*/ */
@ -160,14 +162,25 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
// directly load data // directly load data
startListFragment(savedInstanceState, importData, null); startListFragment(savedInstanceState, importData, null);
} }
} else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
if (!extras.containsKey(EXTRA_QUERY)) {
Log.e(Constants.TAG, "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query' extra!");
return;
}
String query = extras.getString(EXTRA_QUERY);
// TODO: implement KEYSERVER!
} else { } else {
// Internal actions // Other actions
startListFragment(savedInstanceState, null, null); startListFragment(savedInstanceState, null, null);
if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
getSupportActionBar().setSelectedNavigationItem(1); getSupportActionBar().setSelectedNavigationItem(1);
loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[1]); loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[1]);
} else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) { } else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
// also exposed in AndroidManifest
getSupportActionBar().setSelectedNavigationItem(2); getSupportActionBar().setSelectedNavigationItem(2);
loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[2]); loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[2]);
} else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
@ -239,6 +252,23 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
ft.commit(); ft.commit();
} }
public void loadFromFingerprintUri(Uri dataUri) {
String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH);
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
if (fingerprint.length() < 16) {
Toast.makeText(this, R.string.import_qr_code_too_short_fingerprint,
Toast.LENGTH_LONG).show();
return;
}
Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID);
queryIntent.putExtra(KeyServerQueryActivity.EXTRA_FINGERPRINT, fingerprint);
startActivity(queryIntent);
}
public void loadCallback(byte[] importData, String importFilename) { public void loadCallback(byte[] importData, String importFilename) {
mListFragment.loadNew(importData, importFilename); mListFragment.loadNew(importData, importFilename);
} }
@ -413,19 +443,6 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
} }
} }
public void importOnClick() {
importKeys();
}
// public void signAndUploadOnClick() {
// // first, import!
// // importOnClick(view);
//
// // TODO: implement sign and upload!
// Toast.makeText(ImportKeysActivity.this, "Not implemented right now!", Toast.LENGTH_SHORT)
// .show();
// }
/** /**
* NFC * NFC
*/ */

View File

@ -26,6 +26,7 @@ import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -101,8 +102,8 @@ public class ImportKeysQrCodeFragment extends Fragment {
Log.d(Constants.TAG, "scanResult content: " + scanResult.getContents()); Log.d(Constants.TAG, "scanResult content: " + scanResult.getContents());
// look if it's fingerprint only // look if it's fingerprint only
if (scanResult.getContents().toLowerCase(Locale.ENGLISH).startsWith("openpgp4fpr")) { if (scanResult.getContents().toLowerCase(Locale.ENGLISH).startsWith(ImportKeysActivity.FINGERPRINT_SCHEME)) {
importFingerprint(scanResult.getContents().toLowerCase(Locale.ENGLISH)); importFingerprint(Uri.parse(scanResult.getContents()));
return; return;
} }
@ -128,21 +129,8 @@ public class ImportKeysQrCodeFragment extends Fragment {
} }
} }
private void importFingerprint(String uri) { public void importFingerprint(Uri dataUri) {
String fingerprint = uri.split(":")[1]; mImportActivity.loadFromFingerprintUri(dataUri);
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
if (fingerprint.length() < 16) {
Toast.makeText(getActivity(), R.string.import_qr_code_too_short_fingerprint,
Toast.LENGTH_LONG).show();
return;
}
Intent queryIntent = new Intent(getActivity(), KeyServerQueryActivity.class);
queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID);
queryIntent.putExtra(KeyServerQueryActivity.EXTRA_FINGERPRINT, fingerprint);
startActivity(queryIntent);
} }
private void importParts(String[] parts) { private void importParts(String[] parts) {

View File

@ -32,10 +32,12 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysServerFragment extends Fragment { public class ImportKeysServerFragment extends Fragment {
private BootstrapButton mButton; private BootstrapButton mButton;
String mQuery;
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static ImportKeysServerFragment newInstance() { public static ImportKeysServerFragment newInstance(String query) {
ImportKeysServerFragment frag = new ImportKeysServerFragment(); ImportKeysServerFragment frag = new ImportKeysServerFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();

View File

@ -83,7 +83,7 @@ public class ImportKeysListEntry implements Serializable {
this.revoked = pgpKeyRing.getPublicKey().isRevoked(); this.revoked = pgpKeyRing.getPublicKey().isRevoked();
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
.getFingerprint()); .getFingerprint(), true);
this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId); this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();

View File

@ -40,12 +40,14 @@ import android.widget.TextView;
import com.actionbarsherlock.app.SherlockDialogFragment; import com.actionbarsherlock.app.SherlockDialogFragment;
public class ShareQrCodeDialogFragment extends SherlockDialogFragment { public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
private static final String ARG_URI = "uri"; private static final String ARG_KEY_URI = "uri";
private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only"; private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only";
private ImageView mImage; private ImageView mImage;
private TextView mText; private TextView mText;
private boolean mFingerprintOnly;
private ArrayList<String> mContentList; private ArrayList<String> mContentList;
private int mCounter; private int mCounter;
@ -53,15 +55,11 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
/** /**
* Creates new instance of this dialog fragment * Creates new instance of this dialog fragment
*
* @param content
* Content to be shared via QR Codes
* @return
*/ */
public static ShareQrCodeDialogFragment newInstance(Uri dataUri, boolean fingerprintOnly) { public static ShareQrCodeDialogFragment newInstance(Uri dataUri, boolean fingerprintOnly) {
ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment(); ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ARG_URI, dataUri); args.putParcelable(ARG_KEY_URI, dataUri);
args.putBoolean(ARG_FINGERPRINT_ONLY, fingerprintOnly); args.putBoolean(ARG_FINGERPRINT_ONLY, fingerprintOnly);
frag.setArguments(args); frag.setArguments(args);
@ -76,8 +74,8 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity(); final Activity activity = getActivity();
Uri dataUri = getArguments().getParcelable(ARG_URI); Uri dataUri = getArguments().getParcelable(ARG_KEY_URI);
boolean fingerprintOnly = getArguments().getBoolean(ARG_FINGERPRINT_ONLY); mFingerprintOnly = getArguments().getBoolean(ARG_FINGERPRINT_ONLY);
AlertDialog.Builder alert = new AlertDialog.Builder(activity); AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@ -90,29 +88,31 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image); mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image);
mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text); mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text);
// TODO
long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
String content = null; String content = null;
if (fingerprintOnly) { if (mFingerprintOnly) {
content = "openpgp4fpr:"; content = "openpgp4fpr:";
String fingerprint = PgpKeyHelper.convertKeyToHex(masterKeyId); byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " "
+ fingerprint);
mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
content = content + fingerprint; content = content + fingerprint;
Log.d(Constants.TAG, "content: " + content); Log.d(Constants.TAG, "content: " + content);
alert.setPositiveButton(R.string.btn_okay, null); alert.setPositiveButton(R.string.btn_okay, null);
setQrCode(content);
} else { } else {
mText.setText(R.string.share_qr_code_dialog_start); mText.setText(R.string.share_qr_code_dialog_start);
// TODO
long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
// get public keyring as ascii armored string // get public keyring as ascii armored string
ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString( ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
getActivity(), dataUri, new long[] { masterKeyId }); getActivity(), dataUri, new long[]{masterKeyId});
// TODO: binary? // TODO: binary?
@ -122,55 +122,61 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
// http://stackoverflow.com/questions/2620444/how-to-prevent-a-dialog-from-closing-when-a-button-is-clicked // http://stackoverflow.com/questions/2620444/how-to-prevent-a-dialog-from-closing-when-a-button-is-clicked
alert.setPositiveButton(R.string.btn_next, null); alert.setPositiveButton(R.string.btn_next, null);
alert.setNegativeButton(android.R.string.cancel, null); alert.setNegativeButton(android.R.string.cancel, null);
mContentList = splitString(content, 1000);
// start with first
mCounter = 0;
updatePartsQrCode();
} }
mContentList = splitString(content, 1000);
// start with first
mCounter = 0;
updateQrCode();
return alert.create(); return alert.create();
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
AlertDialog alertDialog = (AlertDialog) getDialog();
final Button backButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
final Button nextButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
backButton.setOnClickListener(new View.OnClickListener() { if (!mFingerprintOnly) {
@Override AlertDialog alertDialog = (AlertDialog) getDialog();
public void onClick(View v) { final Button backButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
if (mCounter > 0) { final Button nextButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
mCounter--;
updateQrCode();
updateDialog(backButton, nextButton);
} else {
dismiss();
}
}
});
nextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCounter < mContentList.size() - 1) { backButton.setOnClickListener(new View.OnClickListener() {
mCounter++; @Override
updateQrCode(); public void onClick(View v) {
updateDialog(backButton, nextButton); if (mCounter > 0) {
} else { mCounter--;
dismiss(); updatePartsQrCode();
updateDialog(backButton, nextButton);
} else {
dismiss();
}
} }
} });
}); nextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCounter < mContentList.size() - 1) {
mCounter++;
updatePartsQrCode();
updateDialog(backButton, nextButton);
} else {
dismiss();
}
}
});
}
} }
private void updateQrCode() { private void updatePartsQrCode() {
// Content: <counter>,<size>,<content> // Content: <counter>,<size>,<content>
mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(mCounter + "," + mContentList.size() setQrCode(mCounter + "," + mContentList.size() + "," + mContentList.get(mCounter));
+ "," + mContentList.get(mCounter), QR_CODE_SIZE)); }
private void setQrCode(String data) {
mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE));
} }
private void updateDialog(Button backButton, Button nextButton) { private void updateDialog(Button backButton, Button nextButton) {

View File

@ -3,8 +3,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_marginLeft="16dp" android:paddingLeft="16dp"
android:layout_marginRight="16dp" android:paddingRight="16dp"
android:orientation="vertical" > android:orientation="vertical" >
<TextView <TextView

View File

@ -8,7 +8,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_clipboard_button" android:id="@+id/import_clipboard_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="70dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:text="@string/import_clipboard_button" android:text="@string/import_clipboard_button"
bootstrapbutton:bb_icon_left="fa-clipboard" bootstrapbutton:bb_icon_left="fa-clipboard"

View File

@ -7,7 +7,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_keyserver_button" android:id="@+id/import_keyserver_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="70dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:text="@string/menu_key_server" android:text="@string/menu_key_server"
bootstrapbutton:bb_size="default" bootstrapbutton:bb_size="default"

View File

@ -3,13 +3,13 @@
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:padding="10dp"
android:orientation="horizontal" > android:orientation="horizontal" >
<com.beardedhen.androidbootstrap.BootstrapButton <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_nfc_button" android:id="@+id/import_nfc_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="60dp" android:layout_height="70dp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"

View File

@ -8,7 +8,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_qrcode_button" android:id="@+id/import_qrcode_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="70dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:text="@string/import_qr_scan_button" android:text="@string/import_qr_scan_button"
bootstrapbutton:bb_icon_left="fa-barcode" bootstrapbutton:bb_icon_left="fa-barcode"

View File

@ -8,8 +8,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:paddingLeft="16dp"
android:layout_marginRight="16dp" android:paddingRight="16dp"
android:orientation="vertical" > android:orientation="vertical" >
<TextView <TextView

View File

@ -8,8 +8,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:paddingLeft="16dp"
android:layout_marginRight="16dp" android:paddingRight="16dp"
android:orientation="vertical" > android:orientation="vertical" >
<TextView <TextView

View File

@ -57,12 +57,12 @@ I am using the newest [Android Studio](http://developer.android.com/sdk/installi
All Intents require user interaction, e.g. to finally encrypt the user needs to press the "Encrypt" button. All Intents require user interaction, e.g. to finally encrypt the user needs to press the "Encrypt" button.
To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API. To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
#### Android Intent actions provided by OpenPGP Keychain: #### Android Intent actions:
* ``android.intent.action.VIEW`` connected to .gpg and .asc files: Import Key and Decrypt * ``android.intent.action.VIEW`` connected to .gpg and .asc files: Import Key and Decrypt
* ``android.intent.action.SEND`` connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt * ``android.intent.action.SEND`` connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
#### OpenPGP Keychain specific Intent actions: #### OpenPGP Keychain Intent actions:
* ``org.sufficientlysecure.keychain.action.ENCRYPT`` * ``org.sufficientlysecure.keychain.action.ENCRYPT``
* To encrypt or sign text, use extra ``text`` (type: ``String``) * To encrypt or sign text, use extra ``text`` (type: ``String``)
@ -74,9 +74,15 @@ To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY`` * ``org.sufficientlysecure.keychain.action.IMPORT_KEY``
* Extras: ``key_bytes`` (type: ``byte[]``) * Extras: ``key_bytes`` (type: ``byte[]``)
* or set data ``Uri`` (``intent.setData()``) pointing to a file * or set data ``Uri`` (``intent.setData()``) pointing to a file
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER``
* Extras: ``query`` (type: ``String``)
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE`` * ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE``
* without extras, starts Barcode Scanner to get QR Code * without extras, starts Barcode Scanner to get QR Code
#### OpenPGP Keychain special registered Intents:
* ``android.intent.action.VIEW`` with URIs following the ``openpgp4fpr`` schema. For example: ``openpgp4fpr:718C070100012282``. This is used in QR Codes, but could also be embedded into your website. (compatible with Monkeysphere's and Guardian Project's QR Codes)
* NFC (``android.nfc.action.NDEF_DISCOVERED``) on mime type ``application/pgp-keys`` (as specified in http://tools.ietf.org/html/rfc3156, section 7)
### OpenPGP Remote API ### OpenPGP Remote API
To do asyncronous fast encryption/decryption/sign/verify operations bind to the OpenPGP remote service. To do asyncronous fast encryption/decryption/sign/verify operations bind to the OpenPGP remote service.
The API Demo contains all required AIDL files and a demo activity. The API Demo contains all required AIDL files and a demo activity.