From 4b5de13e4297d688901e8070f627a9a213097bb5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 20:36:37 +0100 Subject: certification of the first linked identity packet! --- .../pgp/affirmation/AffirmationResource.java | 17 +- .../keychain/pgp/affirmation/LinkedIdentity.java | 22 ++- .../resources/GenericHttpsResource.java | 5 +- .../keychain/ui/PassphraseDialogActivity.java | 3 + .../AffirmationCreateHttpsStep2Fragment.java | 184 +++++++++++++++++---- .../AffirmationCreateTwitterStep1Fragment.java | 1 + .../AffirmationCreateTwitterStep2Fragment.java | 1 + 7 files changed, 188 insertions(+), 45 deletions(-) (limited to 'OpenKeychain/src/main/java/org') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java index 45919a89a..ffe89931a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -13,13 +13,14 @@ import org.sufficientlysecure.keychain.util.Log; import java.net.URI; import java.util.HashMap; +import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public abstract class AffirmationResource { - protected final URI mUri; + protected final URI mSubUri; protected final Set mFlags; protected final HashMap mParams; @@ -29,7 +30,19 @@ public abstract class AffirmationResource { protected AffirmationResource(Set flags, HashMap params, URI uri) { mFlags = flags; mParams = params; - mUri = uri; + mSubUri = uri; + } + + public Set getFlags () { + return new HashSet(mFlags); + } + + public HashMap getParams () { + return new HashMap(mParams); + } + + public URI getSubUri () { + return mSubUri; } public static String generate (Context context, byte[] fingerprint, String nonce) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java index 1e27b2c64..ee9933da3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java @@ -1,12 +1,13 @@ package org.sufficientlysecure.keychain.pgp.affirmation; import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.util.Strings; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.util.Log; -import java.io.Serializable; import java.net.URI; import java.util.Arrays; import java.util.HashMap; @@ -15,7 +16,7 @@ import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; -public class LinkedIdentity implements Serializable { +public class LinkedIdentity { protected byte[] mData; public final String mNonce; @@ -41,7 +42,7 @@ public class LinkedIdentity implements Serializable { this(null, nonce, flags, params, subUri); } - public byte[] encode() { + public byte[] getEncoded() { if (mData != null) { return mData; } @@ -79,11 +80,14 @@ public class LinkedIdentity implements Serializable { b.append(mSubUri); byte[] nonceBytes = Hex.decode(mNonce); + if (nonceBytes.length != 12) { + throw new AssertionError("nonce must be 12 bytes"); + } byte[] data = Strings.toUTF8ByteArray(b.toString()); byte[] result = new byte[data.length+12]; System.arraycopy(nonceBytes, 0, result, 0, 12); - System.arraycopy(data, 0, result, 12, result.length); + System.arraycopy(data, 0, result, 12, data.length); return result; } @@ -91,7 +95,7 @@ public class LinkedIdentity implements Serializable { /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the * subpacket can not be parsed as a valid affirmation. */ - public static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { + static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { if (subpacket.getType() != 100) { return null; } @@ -148,6 +152,14 @@ public class LinkedIdentity implements Serializable { } + public static LinkedIdentity fromResource (AffirmationResource res, String nonce) { + return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri()); + } + + public WrappedUserAttribute toUserAttribute () { + return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); + } + public static String generateNonce() { // TODO make this actually random // byte[] data = new byte[96]; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java index 8f4d0c41b..74c0689b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -9,6 +9,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -38,13 +39,13 @@ public class GenericHttpsResource extends AffirmationResource { @Override protected String fetchResource (OperationLog log, int indent) { - log.add(LogType.MSG_LV_FETCH, indent, mUri.toString()); + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); indent += 1; try { HttpsURLConnection conn = null; - URL url = mUri.toURL(); + URL url = mSubUri.toURL(); int status = 0; int redirects = 0; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index fd9324992..5bface1fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -214,6 +214,9 @@ public class PassphraseDialogActivity extends FragmentActivity { case DIVERT_TO_CARD: message = getString(R.string.yubikey_pin_for, userId); break; + // special case: empty passphrase just returns the empty passphrase + case PASSPHRASE_EMPTY: + finishCaching(""); default: message = "This should not happen!"; break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java index 2f9b77f4a..eb1088989 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain.ui.affirmations; +import android.app.Activity; +import android.app.ProgressDialog; import android.content.Intent; import android.graphics.PorterDuff; import android.net.Uri; @@ -24,6 +26,8 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Message; +import android.os.Messenger; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; @@ -36,7 +40,16 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.EditKeyActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; @@ -50,6 +63,7 @@ import java.net.URISyntaxException; public class AffirmationCreateHttpsStep2Fragment extends Fragment { private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; @@ -63,6 +77,9 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { String mResourceUri; String mResourceNonce, mResourceString; + // This is a resource, set AFTER it has been verified + GenericHttpsResource mVerifiedResource = null; + /** * Creates new instance of this fragment */ @@ -91,11 +108,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - - // AffirmationCreateHttpsStep2Fragment frag = - // AffirmationCreateHttpsStep2Fragment.newInstance(); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + startCertify(); } }); @@ -149,7 +162,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { public void setVerifyProgress(boolean on, Boolean success) { mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); @@ -168,6 +181,46 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { } } + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + public void proofVerify() { setVerifyProgress(true, null); @@ -186,6 +239,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { super.onPostExecute(result); if (result.success()) { setVerifyProgress(false, true); + mVerifiedResource = resource; } else { setVerifyProgress(false, false); // on error, show error message @@ -199,49 +253,100 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { } - private void proofSend() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } + public void startCertify() { - private void proofSave () { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); return; } - String targetName = "pgpkey.txt"; + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File targetFile = new File(Constants.Path.APP_DIR, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); - } } - private void saveFile(Uri uri) { - try { - PrintWriter out = - new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mResourceString); - if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + public void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceUri, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } } - } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); - e.printStackTrace(); - } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mAffirmationWizard.mMasterKeyId, mAffirmationWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { + // For saving a file case REQUEST_CODE_OUTPUT: if (data == null) { return; @@ -249,6 +354,13 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { Uri uri = data.getData(); saveFile(uri); break; + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; } super.onActivityResult(requestCode, resultCode, data); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java index 995823499..f52a18807 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import java.io.IOException; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java index fad8cadd4..e12d2f86b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java @@ -31,6 +31,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity; public class AffirmationCreateTwitterStep2Fragment extends Fragment { -- cgit v1.2.3