certification of the first linked identity packet!

This commit is contained in:
Vincent Breitmoser 2015-01-13 20:36:37 +01:00
parent 08ea4e1b7e
commit 4b5de13e42
8 changed files with 191 additions and 47 deletions

View File

@ -13,13 +13,14 @@ import org.sufficientlysecure.keychain.util.Log;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public abstract class AffirmationResource { public abstract class AffirmationResource {
protected final URI mUri; protected final URI mSubUri;
protected final Set<String> mFlags; protected final Set<String> mFlags;
protected final HashMap<String,String> mParams; protected final HashMap<String,String> mParams;
@ -29,7 +30,19 @@ public abstract class AffirmationResource {
protected AffirmationResource(Set<String> flags, HashMap<String,String> params, URI uri) { protected AffirmationResource(Set<String> flags, HashMap<String,String> params, URI uri) {
mFlags = flags; mFlags = flags;
mParams = params; mParams = params;
mUri = uri; mSubUri = uri;
}
public Set<String> getFlags () {
return new HashSet<String>(mFlags);
}
public HashMap<String,String> getParams () {
return new HashMap<String,String>(mParams);
}
public URI getSubUri () {
return mSubUri;
} }
public static String generate (Context context, byte[] fingerprint, String nonce) { public static String generate (Context context, byte[] fingerprint, String nonce) {

View File

@ -1,12 +1,13 @@
package org.sufficientlysecure.keychain.pgp.affirmation; package org.sufficientlysecure.keychain.pgp.affirmation;
import org.spongycastle.bcpg.UserAttributeSubpacket; import org.spongycastle.bcpg.UserAttributeSubpacket;
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.util.Strings; import org.spongycastle.util.Strings;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.io.Serializable;
import java.net.URI; import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -15,7 +16,7 @@ import java.util.Iterator;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
public class LinkedIdentity implements Serializable { public class LinkedIdentity {
protected byte[] mData; protected byte[] mData;
public final String mNonce; public final String mNonce;
@ -41,7 +42,7 @@ public class LinkedIdentity implements Serializable {
this(null, nonce, flags, params, subUri); this(null, nonce, flags, params, subUri);
} }
public byte[] encode() { public byte[] getEncoded() {
if (mData != null) { if (mData != null) {
return mData; return mData;
} }
@ -79,11 +80,14 @@ public class LinkedIdentity implements Serializable {
b.append(mSubUri); b.append(mSubUri);
byte[] nonceBytes = Hex.decode(mNonce); byte[] nonceBytes = Hex.decode(mNonce);
if (nonceBytes.length != 12) {
throw new AssertionError("nonce must be 12 bytes");
}
byte[] data = Strings.toUTF8ByteArray(b.toString()); byte[] data = Strings.toUTF8ByteArray(b.toString());
byte[] result = new byte[data.length+12]; byte[] result = new byte[data.length+12];
System.arraycopy(nonceBytes, 0, result, 0, 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; return result;
} }
@ -91,7 +95,7 @@ public class LinkedIdentity implements Serializable {
/** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the
* subpacket can not be parsed as a valid affirmation. * subpacket can not be parsed as a valid affirmation.
*/ */
public static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) {
if (subpacket.getType() != 100) { if (subpacket.getType() != 100) {
return null; 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() { public static String generateNonce() {
// TODO make this actually random // TODO make this actually random
// byte[] data = new byte[96]; // byte[] data = new byte[96];

View File

@ -9,6 +9,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; 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.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -38,13 +39,13 @@ public class GenericHttpsResource extends AffirmationResource {
@Override @Override
protected String fetchResource (OperationLog log, int indent) { 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; indent += 1;
try { try {
HttpsURLConnection conn = null; HttpsURLConnection conn = null;
URL url = mUri.toURL(); URL url = mSubUri.toURL();
int status = 0; int status = 0;
int redirects = 0; int redirects = 0;

View File

@ -214,6 +214,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
case DIVERT_TO_CARD: case DIVERT_TO_CARD:
message = getString(R.string.yubikey_pin_for, userId); message = getString(R.string.yubikey_pin_for, userId);
break; break;
// special case: empty passphrase just returns the empty passphrase
case PASSPHRASE_EMPTY:
finishCaching("");
default: default:
message = "This should not happen!"; message = "This should not happen!";
break; break;

View File

@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.ui.affirmations; package org.sufficientlysecure.keychain.ui.affirmations;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.net.Uri; import android.net.Uri;
@ -24,6 +26,8 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -36,7 +40,16 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; 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.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;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.FileHelper;
@ -50,6 +63,7 @@ import java.net.URISyntaxException;
public class AffirmationCreateHttpsStep2Fragment extends Fragment { public class AffirmationCreateHttpsStep2Fragment extends Fragment {
private static final int REQUEST_CODE_OUTPUT = 0x00007007; 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"; public static final String URI = "uri", NONCE = "nonce", TEXT = "text";
@ -63,6 +77,9 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
String mResourceUri; String mResourceUri;
String mResourceNonce, mResourceString; String mResourceNonce, mResourceString;
// This is a resource, set AFTER it has been verified
GenericHttpsResource mVerifiedResource = null;
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
@ -91,11 +108,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
startCertify();
// AffirmationCreateHttpsStep2Fragment frag =
// AffirmationCreateHttpsStep2Fragment.newInstance();
// mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT);
} }
}); });
@ -168,38 +181,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
} }
} }
public void proofVerify() { private void proofSend () {
setVerifyProgress(true, null);
try {
final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri));
new AsyncTask<Void,Void,LinkedVerifyResult>() {
@Override
protected LinkedVerifyResult doInBackground(Void... params) {
return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce);
}
@Override
protected void onPostExecute(LinkedVerifyResult result) {
super.onPostExecute(result);
if (result.success()) {
setVerifyProgress(false, true);
} else {
setVerifyProgress(false, false);
// on error, show error message
result.createNotify(getActivity()).show();
}
}
}.execute();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private void proofSend() {
Intent sendIntent = new Intent(); Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString);
@ -239,9 +221,132 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
} }
} }
public void proofVerify() {
setVerifyProgress(true, null);
try {
final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri));
new AsyncTask<Void,Void,LinkedVerifyResult>() {
@Override
protected LinkedVerifyResult doInBackground(Void... params) {
return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce);
}
@Override
protected void onPostExecute(LinkedVerifyResult result) {
super.onPostExecute(result);
if (result.success()) {
setVerifyProgress(false, true);
mVerifiedResource = resource;
} else {
setVerifyProgress(false, false);
// on error, show error message
result.createNotify(getActivity()).show();
}
}
}.execute();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public void startCertify() {
if (mVerifiedResource == null) {
Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR);
return;
}
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId);
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
}
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);
}
}
};
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 @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
// For saving a file
case REQUEST_CODE_OUTPUT: case REQUEST_CODE_OUTPUT:
if (data == null) { if (data == null) {
return; return;
@ -249,6 +354,13 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
Uri uri = data.getData(); Uri uri = data.getData();
saveFile(uri); saveFile(uri);
break; 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); super.onActivityResult(requestCode, resultCode, data);
} }

View File

@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import java.io.IOException; import java.io.IOException;

View File

@ -31,6 +31,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
public class AffirmationCreateTwitterStep2Fragment extends Fragment { public class AffirmationCreateTwitterStep2Fragment extends Fragment {

View File

@ -1162,5 +1162,6 @@
<string name="linked_verify_success">Verification successful!</string> <string name="linked_verify_success">Verification successful!</string>
<string name="linked_verify_error">Veriication error!</string> <string name="linked_verify_error">Veriication error!</string>
<string name="linked_verify_pending">Not yet verified</string> <string name="linked_verify_pending">Not yet verified</string>
<string name="linked_need_verify">The resource needs to be verified before you can proceed!</string>
</resources> </resources>