mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-12-26 00:48:51 -05:00
yubikey certifications!
This commit is contained in:
parent
aca54e31ea
commit
d46fc3740b
@ -30,6 +30,8 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
|||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
@ -38,6 +40,8 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException
|
|||||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
||||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel.NfcSignOperationsBuilder;
|
||||||
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;
|
||||||
|
|
||||||
@ -73,10 +77,6 @@ public class CertifyOperation extends BaseOperation {
|
|||||||
mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId);
|
mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId);
|
||||||
log.add(LogType.MSG_CRT_UNLOCK, 1);
|
log.add(LogType.MSG_CRT_UNLOCK, 1);
|
||||||
certificationKey = secretKeyRing.getSecretKey();
|
certificationKey = secretKeyRing.getSecretKey();
|
||||||
if (certificationKey.getSecretKeyType() == SecretKeyType.DIVERT_TO_CARD) {
|
|
||||||
log.add(LogType.MSG_CRT_ERROR_DIVERT, 2);
|
|
||||||
return new CertifyResult(CertifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
// certification is always with the master key id, so use that one
|
// certification is always with the master key id, so use that one
|
||||||
String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
|
String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
|
||||||
@ -102,6 +102,8 @@ public class CertifyOperation extends BaseOperation {
|
|||||||
|
|
||||||
int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0;
|
int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0;
|
||||||
|
|
||||||
|
NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(parcel.getSignatureTime());
|
||||||
|
|
||||||
// Work through all requested certifications
|
// Work through all requested certifications
|
||||||
for (CertifyAction action : parcel.mCertifyActions) {
|
for (CertifyAction action : parcel.mCertifyActions) {
|
||||||
|
|
||||||
@ -122,28 +124,21 @@ public class CertifyOperation extends BaseOperation {
|
|||||||
CanonicalizedPublicKeyRing publicRing =
|
CanonicalizedPublicKeyRing publicRing =
|
||||||
mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId);
|
mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId);
|
||||||
|
|
||||||
UncachedKeyRing certifiedKey = null;
|
PgpCertifyOperation op = new PgpCertifyOperation();
|
||||||
if (action.mUserIds != null) {
|
PgpCertifyResult result = op.certify(certificationKey, publicRing,
|
||||||
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(),
|
log, 2, action, parcel.getSignatureData(), parcel.getSignatureTime());
|
||||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
|
||||||
|
|
||||||
certifiedKey = certificationKey.certifyUserIds(
|
if (!result.success()) {
|
||||||
publicRing, action.mUserIds, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.mUserAttributes != null) {
|
|
||||||
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(),
|
|
||||||
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
|
||||||
|
|
||||||
certifiedKey = certificationKey.certifyUserAttributes(
|
|
||||||
publicRing, action.mUserAttributes, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (certifiedKey == null) {
|
|
||||||
certifyError += 1;
|
certifyError += 1;
|
||||||
log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3);
|
continue;
|
||||||
}
|
}
|
||||||
certifiedKeys.add(certifiedKey);
|
if (result.nfcInputRequired()) {
|
||||||
|
NfcOperationsParcel requiredInput = result.getRequiredInput();
|
||||||
|
allRequiredInput.addAll(requiredInput);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
certifiedKeys.add(result.getCertifiedRing());
|
||||||
|
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
certifyError += 1;
|
certifyError += 1;
|
||||||
@ -152,6 +147,11 @@ public class CertifyOperation extends BaseOperation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! allRequiredInput.isEmpty()) {
|
||||||
|
log.add(LogType.MSG_CRT_NFC_RETURN, 1);
|
||||||
|
return new CertifyResult(log, allRequiredInput.build());
|
||||||
|
}
|
||||||
|
|
||||||
log.add(LogType.MSG_CRT_SAVING, 1);
|
log.add(LogType.MSG_CRT_SAVING, 1);
|
||||||
|
|
||||||
// Check if we were cancelled
|
// Check if we were cancelled
|
||||||
|
@ -23,6 +23,7 @@ import android.content.Intent;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
|
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
|
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
@ -30,16 +31,19 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
|||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
|
|
||||||
public class CertifyResult extends OperationResult {
|
public class CertifyResult extends InputPendingResult {
|
||||||
|
|
||||||
int mCertifyOk, mCertifyError, mUploadOk, mUploadError;
|
int mCertifyOk, mCertifyError, mUploadOk, mUploadError;
|
||||||
|
|
||||||
public CertifyResult(int result, OperationLog log) {
|
public CertifyResult(int result, OperationLog log) {
|
||||||
super(result, log);
|
super(result, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CertifyResult(OperationLog log, NfcOperationsParcel requiredInput) {
|
||||||
|
super(log, requiredInput);
|
||||||
|
}
|
||||||
|
|
||||||
public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError, int uploadOk, int uploadError) {
|
public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError, int uploadOk, int uploadError) {
|
||||||
this(result, log);
|
super(result, log);
|
||||||
mCertifyOk = certifyOk;
|
mCertifyOk = certifyOk;
|
||||||
mCertifyError = certifyError;
|
mCertifyError = certifyError;
|
||||||
mUploadOk = uploadOk;
|
mUploadOk = uploadOk;
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
package org.sufficientlysecure.keychain.operations.results;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
|
|
||||||
|
|
||||||
|
public class InputPendingResult extends OperationResult {
|
||||||
|
|
||||||
|
// the fourth bit indicates a "data pending" result! (it's also a form of non-success)
|
||||||
|
public static final int RESULT_PENDING = RESULT_ERROR + 8;
|
||||||
|
|
||||||
|
public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16;
|
||||||
|
public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32;
|
||||||
|
|
||||||
|
final NfcOperationsParcel mRequiredInput;
|
||||||
|
final Long mKeyIdPassphraseNeeded;
|
||||||
|
|
||||||
|
public InputPendingResult(int result, OperationLog log) {
|
||||||
|
super(result, log);
|
||||||
|
mRequiredInput = null;
|
||||||
|
mKeyIdPassphraseNeeded = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPendingResult(OperationLog log, NfcOperationsParcel requiredInput) {
|
||||||
|
super(RESULT_PENDING_NFC, log);
|
||||||
|
mRequiredInput = requiredInput;
|
||||||
|
mKeyIdPassphraseNeeded = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPendingResult(OperationLog log, long keyIdPassphraseNeeded) {
|
||||||
|
super(RESULT_PENDING_PASSPHRASE, log);
|
||||||
|
mRequiredInput = null;
|
||||||
|
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPendingResult(Parcel source) {
|
||||||
|
super(source);
|
||||||
|
mRequiredInput = source.readParcelable(getClass().getClassLoader());
|
||||||
|
mKeyIdPassphraseNeeded = source.readInt() != 0 ? source.readLong() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
super.writeToParcel(dest, flags);
|
||||||
|
dest.writeParcelable(mRequiredInput, 0);
|
||||||
|
if (mKeyIdPassphraseNeeded != null) {
|
||||||
|
dest.writeInt(1);
|
||||||
|
dest.writeLong(mKeyIdPassphraseNeeded);
|
||||||
|
} else {
|
||||||
|
dest.writeInt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPending() {
|
||||||
|
return (mResult & RESULT_PENDING) == RESULT_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNfcPending() {
|
||||||
|
return (mResult & RESULT_PENDING_NFC) == RESULT_PENDING_NFC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPassphrasePending() {
|
||||||
|
return (mResult & RESULT_PENDING_PASSPHRASE) == RESULT_PENDING_PASSPHRASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NfcOperationsParcel getNfcOperationsParcel() {
|
||||||
|
return mRequiredInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPassphraseKeyId() {
|
||||||
|
return mKeyIdPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -697,9 +697,9 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),
|
MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),
|
||||||
MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),
|
MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),
|
||||||
MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock),
|
MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock),
|
||||||
MSG_CRT_ERROR_DIVERT (LogLevel.ERROR, R.string.msg_crt_error_divert),
|
|
||||||
MSG_CRT (LogLevel.START, R.string.msg_crt),
|
MSG_CRT (LogLevel.START, R.string.msg_crt),
|
||||||
MSG_CRT_MASTER_FETCH (LogLevel.DEBUG, R.string.msg_crt_master_fetch),
|
MSG_CRT_MASTER_FETCH (LogLevel.DEBUG, R.string.msg_crt_master_fetch),
|
||||||
|
MSG_CRT_NFC_RETURN (LogLevel.OK, R.string.msg_crt_nfc_return),
|
||||||
MSG_CRT_SAVE (LogLevel.DEBUG, R.string.msg_crt_save),
|
MSG_CRT_SAVE (LogLevel.DEBUG, R.string.msg_crt_save),
|
||||||
MSG_CRT_SAVING (LogLevel.DEBUG, R.string.msg_crt_saving),
|
MSG_CRT_SAVING (LogLevel.DEBUG, R.string.msg_crt_saving),
|
||||||
MSG_CRT_SUCCESS (LogLevel.OK, R.string.msg_crt_success),
|
MSG_CRT_SUCCESS (LogLevel.OK, R.string.msg_crt_success),
|
||||||
|
@ -31,6 +31,7 @@ public class SignEncryptResult extends OperationResult {
|
|||||||
|
|
||||||
public static final int RESULT_PENDING = RESULT_ERROR + 8;
|
public static final int RESULT_PENDING = RESULT_ERROR + 8;
|
||||||
|
|
||||||
|
|
||||||
public PgpSignEncryptResult getPending() {
|
public PgpSignEncryptResult getPending() {
|
||||||
for (PgpSignEncryptResult sub : mResults) {
|
for (PgpSignEncryptResult sub : mResults) {
|
||||||
if (sub.isPending()) {
|
if (sub.isPending()) {
|
||||||
|
@ -202,7 +202,25 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
|
public PGPSignatureGenerator getCertSignatureGenerator(Map<ByteBuffer, byte[]> signedHashes) {
|
||||||
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
||||||
|
PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
|
||||||
|
|
||||||
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
|
throw new PrivateKeyNotUnlockedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
|
try {
|
||||||
|
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
||||||
|
return signatureGenerator;
|
||||||
|
} catch (PGPException e) {
|
||||||
|
Log.e(Constants.TAG, "signing error", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSignatureGenerator getDataSignatureGenerator(int hashAlgo, boolean cleartext,
|
||||||
Map<ByteBuffer, byte[]> signedHashes, Date creationTimestamp)
|
Map<ByteBuffer, byte[]> signedHashes, Date creationTimestamp)
|
||||||
throws PgpGeneralException {
|
throws PgpGeneralException {
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
@ -259,135 +277,6 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Certify the given pubkeyid with the given masterkeyid.
|
|
||||||
*
|
|
||||||
* @param publicKeyRing Keyring to add certification to.
|
|
||||||
* @param userIds User IDs to certify
|
|
||||||
* @return A keyring with added certifications
|
|
||||||
*/
|
|
||||||
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing,
|
|
||||||
List<String> userIds,
|
|
||||||
HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {
|
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
|
||||||
throw new PrivateKeyNotUnlockedException();
|
|
||||||
}
|
|
||||||
if (!isMasterKey()) {
|
|
||||||
throw new AssertionError("tried to certify with non-master key, this is a programming error!");
|
|
||||||
}
|
|
||||||
if (publicKeyRing.getMasterKeyId() == getKeyId()) {
|
|
||||||
throw new AssertionError("key tried to self-certify, this is a programming error!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
|
||||||
PGPSignatureGenerator signatureGenerator;
|
|
||||||
{
|
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
|
||||||
PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
|
|
||||||
|
|
||||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
|
||||||
try {
|
|
||||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
|
||||||
} catch (PGPException e) {
|
|
||||||
Log.e(Constants.TAG, "signing error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // supply signatureGenerator with a SubpacketVector
|
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
|
||||||
if (creationTimestamp != null) {
|
|
||||||
spGen.setSignatureCreationTime(false, creationTimestamp);
|
|
||||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
|
|
||||||
}
|
|
||||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
|
||||||
signatureGenerator.setHashedSubpackets(packetVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the master subkey (which we certify for)
|
|
||||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
|
||||||
|
|
||||||
// fetch public key ring, add the certification and return it
|
|
||||||
try {
|
|
||||||
for (String userId : userIds) {
|
|
||||||
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
|
||||||
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
Log.e(Constants.TAG, "signing error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
|
|
||||||
|
|
||||||
return new UncachedKeyRing(ring);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Certify the given user attributes with the given masterkeyid.
|
|
||||||
*
|
|
||||||
* @param publicKeyRing Keyring to add certification to.
|
|
||||||
* @param userAttributes User IDs to certify, or all if null
|
|
||||||
* @return A keyring with added certifications
|
|
||||||
*/
|
|
||||||
public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing,
|
|
||||||
List<WrappedUserAttribute> userAttributes,
|
|
||||||
HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {
|
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
|
||||||
throw new PrivateKeyNotUnlockedException();
|
|
||||||
}
|
|
||||||
if (!isMasterKey()) {
|
|
||||||
throw new AssertionError("tried to certify with non-master key, this is a programming error!");
|
|
||||||
}
|
|
||||||
if (publicKeyRing.getMasterKeyId() == getKeyId()) {
|
|
||||||
throw new AssertionError("key tried to self-certify, this is a programming error!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
|
||||||
PGPSignatureGenerator signatureGenerator;
|
|
||||||
{
|
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
|
||||||
PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
|
|
||||||
|
|
||||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
|
||||||
try {
|
|
||||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
|
||||||
} catch (PGPException e) {
|
|
||||||
Log.e(Constants.TAG, "signing error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // supply signatureGenerator with a SubpacketVector
|
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
|
||||||
if (creationTimestamp != null) {
|
|
||||||
spGen.setSignatureCreationTime(false, creationTimestamp);
|
|
||||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
|
|
||||||
}
|
|
||||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
|
||||||
signatureGenerator.setHashedSubpackets(packetVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the master subkey (which we certify for)
|
|
||||||
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
|
||||||
|
|
||||||
// fetch public key ring, add the certification and return it
|
|
||||||
try {
|
|
||||||
for (WrappedUserAttribute userAttribute : userAttributes) {
|
|
||||||
PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
|
|
||||||
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);
|
|
||||||
publicKey = PGPPublicKey.addCertification(publicKey, vector, sig);
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
Log.e(Constants.TAG, "signing error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
|
|
||||||
|
|
||||||
return new UncachedKeyRing(ring);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class PrivateKeyNotUnlockedException extends RuntimeException {
|
static class PrivateKeyNotUnlockedException extends RuntimeException {
|
||||||
// this exception is a programming error which happens when an operation which requires
|
// this exception is a programming error which happens when an operation which requires
|
||||||
// the private key is called without a previous call to unlock()
|
// the private key is called without a previous call to unlock()
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.spongycastle.openpgp.PGPException;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
|
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
|
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel.NfcSignOperationsBuilder;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
public class PgpCertifyOperation {
|
||||||
|
|
||||||
|
public PgpCertifyResult certify(
|
||||||
|
CanonicalizedSecretKey secretKey,
|
||||||
|
CanonicalizedPublicKeyRing publicRing,
|
||||||
|
OperationLog log,
|
||||||
|
int indent,
|
||||||
|
CertifyAction action,
|
||||||
|
Map<ByteBuffer,byte[]> signedHashes,
|
||||||
|
Date creationTimestamp) {
|
||||||
|
|
||||||
|
if (!secretKey.isMasterKey()) {
|
||||||
|
throw new AssertionError("tried to certify with non-master key, this is a programming error!");
|
||||||
|
}
|
||||||
|
if (publicRing.getMasterKeyId() == secretKey.getKeyId()) {
|
||||||
|
throw new AssertionError("key tried to self-certify, this is a programming error!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||||
|
PGPSignatureGenerator signatureGenerator = secretKey.getCertSignatureGenerator(signedHashes);
|
||||||
|
|
||||||
|
{ // supply signatureGenerator with a SubpacketVector
|
||||||
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
if (creationTimestamp != null) {
|
||||||
|
spGen.setSignatureCreationTime(false, creationTimestamp);
|
||||||
|
Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
|
||||||
|
}
|
||||||
|
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||||
|
signatureGenerator.setHashedSubpackets(packetVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the master subkey (which we certify for)
|
||||||
|
PGPPublicKey publicKey = publicRing.getPublicKey().getPublicKey();
|
||||||
|
|
||||||
|
NfcSignOperationsBuilder requiredInput = new NfcSignOperationsBuilder(creationTimestamp);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (action.mUserIds != null) {
|
||||||
|
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(),
|
||||||
|
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||||
|
|
||||||
|
// fetch public key ring, add the certification and return it
|
||||||
|
for (String userId : action.mUserIds) {
|
||||||
|
try {
|
||||||
|
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
||||||
|
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
||||||
|
} catch (NfcInteractionNeeded e) {
|
||||||
|
requiredInput.addHash(e.hashToSign, e.hashAlgo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.mUserAttributes != null) {
|
||||||
|
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(),
|
||||||
|
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
|
||||||
|
|
||||||
|
// fetch public key ring, add the certification and return it
|
||||||
|
for (WrappedUserAttribute userAttribute : action.mUserAttributes) {
|
||||||
|
PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
|
||||||
|
try {
|
||||||
|
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);
|
||||||
|
publicKey = PGPPublicKey.addCertification(publicKey, vector, sig);
|
||||||
|
} catch (NfcInteractionNeeded e) {
|
||||||
|
requiredInput.addHash(e.hashToSign, e.hashAlgo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (PGPException e) {
|
||||||
|
Log.e(Constants.TAG, "signing error", e);
|
||||||
|
return new PgpCertifyResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requiredInput.isEmpty()) {
|
||||||
|
return new PgpCertifyResult(requiredInput.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicRing.getRing(), publicKey);
|
||||||
|
return new PgpCertifyResult(new UncachedKeyRing(ring));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PgpCertifyResult {
|
||||||
|
|
||||||
|
final NfcOperationsParcel mRequiredInput;
|
||||||
|
final UncachedKeyRing mCertifiedRing;
|
||||||
|
|
||||||
|
PgpCertifyResult() {
|
||||||
|
mRequiredInput = null;
|
||||||
|
mCertifiedRing = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgpCertifyResult(NfcOperationsParcel requiredInput) {
|
||||||
|
mRequiredInput = requiredInput;
|
||||||
|
mCertifiedRing = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgpCertifyResult(UncachedKeyRing certifiedRing) {
|
||||||
|
mRequiredInput = null;
|
||||||
|
mCertifiedRing = certifiedRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean success() {
|
||||||
|
return mCertifiedRing != null || mRequiredInput != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean nfcInputRequired() {
|
||||||
|
return mRequiredInput != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UncachedKeyRing getCertifiedRing() {
|
||||||
|
return mCertifiedRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NfcOperationsParcel getRequiredInput() {
|
||||||
|
return mRequiredInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -44,8 +44,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
|
||||||
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@ -283,7 +281,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
|
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
|
||||||
signatureGenerator = signingKey.getSignatureGenerator(
|
signatureGenerator = signingKey.getDataSignatureGenerator(
|
||||||
input.getSignatureHashAlgorithm(), cleartext,
|
input.getSignatureHashAlgorithm(), cleartext,
|
||||||
input.getCryptoData(), input.getSignatureTime());
|
input.getCryptoData(), input.getSignatureTime());
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
|
@ -22,8 +22,10 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
@ -42,9 +44,9 @@ public class CertifyActionsParcel implements Parcelable {
|
|||||||
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
|
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
|
||||||
public CryptoInputParcel mCryptoInput;
|
public CryptoInputParcel mCryptoInput;
|
||||||
|
|
||||||
public CertifyActionsParcel(Date operationTime, long masterKeyId) {
|
public CertifyActionsParcel(CryptoInputParcel cryptoInput, long masterKeyId) {
|
||||||
mMasterKeyId = masterKeyId;
|
mMasterKeyId = masterKeyId;
|
||||||
mCryptoInput = new CryptoInputParcel(operationTime);
|
mCryptoInput = cryptoInput != null ? cryptoInput : new CryptoInputParcel(new Date());
|
||||||
mLevel = CertifyLevel.DEFAULT;
|
mLevel = CertifyLevel.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +72,14 @@ public class CertifyActionsParcel implements Parcelable {
|
|||||||
destination.writeSerializable(mCertifyActions);
|
destination.writeSerializable(mCertifyActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<ByteBuffer, byte[]> getSignatureData() {
|
||||||
|
return mCryptoInput.getCryptoData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getSignatureTime() {
|
||||||
|
return mCryptoInput.getSignatureTime();
|
||||||
|
}
|
||||||
|
|
||||||
public static final Creator<CertifyActionsParcel> CREATOR = new Creator<CertifyActionsParcel>() {
|
public static final Creator<CertifyActionsParcel> CREATOR = new Creator<CertifyActionsParcel>() {
|
||||||
public CertifyActionsParcel createFromParcel(final Parcel source) {
|
public CertifyActionsParcel createFromParcel(final Parcel source) {
|
||||||
return new CertifyActionsParcel(source);
|
return new CertifyActionsParcel(source);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
@ -26,6 +27,7 @@ import android.support.v4.app.FragmentManager;
|
|||||||
|
|
||||||
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.CertifyResult;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.sufficientlysecure.keychain.service.input;
|
package org.sufficientlysecure.keychain.service.input;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
@ -14,13 +16,14 @@ public class NfcOperationsParcel implements Parcelable {
|
|||||||
|
|
||||||
public Date mSignatureTime;
|
public Date mSignatureTime;
|
||||||
public final NfcOperationType mType;
|
public final NfcOperationType mType;
|
||||||
public final byte[][] mInputHash;
|
public final byte[][] mInputHashes;
|
||||||
public final int[] mSignAlgo;
|
public final int[] mSignAlgos;
|
||||||
|
|
||||||
private NfcOperationsParcel(NfcOperationType type, byte[] inputHash, int signAlgo, Date signatureTime) {
|
private NfcOperationsParcel(NfcOperationType type, byte[][] inputHashes,
|
||||||
|
int[] signAlgos, Date signatureTime) {
|
||||||
mType = type;
|
mType = type;
|
||||||
mInputHash = new byte[][] { inputHash };
|
mInputHashes = inputHashes;
|
||||||
mSignAlgo = new int[] { signAlgo };
|
mSignAlgos = signAlgos;
|
||||||
mSignatureTime = signatureTime;
|
mSignatureTime = signatureTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +32,11 @@ public class NfcOperationsParcel implements Parcelable {
|
|||||||
|
|
||||||
{
|
{
|
||||||
int count = source.readInt();
|
int count = source.readInt();
|
||||||
mInputHash = new byte[count][];
|
mInputHashes = new byte[count][];
|
||||||
mSignAlgo = new int[count];
|
mSignAlgos = new int[count];
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
mInputHash[i] = source.createByteArray();
|
mInputHashes[i] = source.createByteArray();
|
||||||
mSignAlgo[i] = source.readInt();
|
mSignAlgos[i] = source.readInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,11 +46,13 @@ public class NfcOperationsParcel implements Parcelable {
|
|||||||
|
|
||||||
public static NfcOperationsParcel createNfcSignOperation(
|
public static NfcOperationsParcel createNfcSignOperation(
|
||||||
byte[] inputHash, int signAlgo, Date signatureTime) {
|
byte[] inputHash, int signAlgo, Date signatureTime) {
|
||||||
return new NfcOperationsParcel(NfcOperationType.NFC_SIGN, inputHash, signAlgo, signatureTime);
|
return new NfcOperationsParcel(NfcOperationType.NFC_SIGN,
|
||||||
|
new byte[][] { inputHash }, new int[] { signAlgo }, signatureTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NfcOperationsParcel createNfcDecryptOperation(byte[] inputHash) {
|
public static NfcOperationsParcel createNfcDecryptOperation(byte[] inputHash) {
|
||||||
return new NfcOperationsParcel(NfcOperationType.NFC_DECRYPT, inputHash, 0, null);
|
return new NfcOperationsParcel(NfcOperationType.NFC_DECRYPT,
|
||||||
|
new byte[][] { inputHash }, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -58,10 +63,10 @@ public class NfcOperationsParcel implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeInt(mType.ordinal());
|
dest.writeInt(mType.ordinal());
|
||||||
dest.writeInt(mInputHash.length);
|
dest.writeInt(mInputHashes.length);
|
||||||
for (int i = 0; i < mInputHash.length; i++) {
|
for (int i = 0; i < mInputHashes.length; i++) {
|
||||||
dest.writeByteArray(mInputHash[i]);
|
dest.writeByteArray(mInputHashes[i]);
|
||||||
dest.writeInt(mSignAlgo[i]);
|
dest.writeInt(mSignAlgos[i]);
|
||||||
}
|
}
|
||||||
if (mSignatureTime != null) {
|
if (mSignatureTime != null) {
|
||||||
dest.writeInt(1);
|
dest.writeInt(1);
|
||||||
@ -82,4 +87,50 @@ public class NfcOperationsParcel implements Parcelable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static class NfcSignOperationsBuilder {
|
||||||
|
Date mSignatureTime;
|
||||||
|
ArrayList<Integer> mSignAlgos = new ArrayList<>();
|
||||||
|
ArrayList<byte[]> mInputHashes = new ArrayList<>();
|
||||||
|
|
||||||
|
public NfcSignOperationsBuilder(Date signatureTime) {
|
||||||
|
mSignatureTime = signatureTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NfcOperationsParcel build() {
|
||||||
|
byte[][] inputHashes = new byte[mInputHashes.size()][];
|
||||||
|
mInputHashes.toArray(inputHashes);
|
||||||
|
int[] signAlgos = new int[mSignAlgos.size()];
|
||||||
|
for (int i = 0; i < mSignAlgos.size(); i++) {
|
||||||
|
signAlgos[i] = mSignAlgos.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NfcOperationsParcel(NfcOperationType.NFC_SIGN,
|
||||||
|
inputHashes, signAlgos, mSignatureTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHash(byte[] hash, int algo) {
|
||||||
|
mInputHashes.add(hash);
|
||||||
|
mSignAlgos.add(algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAll(NfcOperationsParcel input) {
|
||||||
|
if (!mSignatureTime.equals(input.mSignatureTime)) {
|
||||||
|
throw new AssertionError("input times must match, this is a programming error!");
|
||||||
|
}
|
||||||
|
if (input.mType != NfcOperationType.NFC_SIGN) {
|
||||||
|
throw new AssertionError("operation types must match, this is a progrmming error!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.addAll(mInputHashes, input.mInputHashes);
|
||||||
|
for (int signAlgo : input.mSignAlgos) {
|
||||||
|
mSignAlgos.add(signAlgo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return mInputHashes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
|||||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
||||||
@ -66,14 +66,11 @@ import org.sufficientlysecure.keychain.util.Preferences;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
|
|
||||||
public class CertifyKeyFragment extends LoaderFragment
|
public class CertifyKeyFragment extends CryptoOperationFragment
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
|
||||||
|
|
||||||
private CheckBox mUploadKeyCheckbox;
|
private CheckBox mUploadKeyCheckbox;
|
||||||
ListView mUserIds;
|
ListView mUserIds;
|
||||||
|
|
||||||
@ -102,9 +99,6 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
// Start out with a progress indicator.
|
|
||||||
setContentShown(false);
|
|
||||||
|
|
||||||
mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(CertifyKeyActivity.EXTRA_KEY_IDS);
|
mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(CertifyKeyActivity.EXTRA_KEY_IDS);
|
||||||
if (mPubMasterKeyIds == null) {
|
if (mPubMasterKeyIds == null) {
|
||||||
Log.e(Constants.TAG, "List of key ids to certify missing!");
|
Log.e(Constants.TAG, "List of key ids to certify missing!");
|
||||||
@ -114,6 +108,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
|
|
||||||
mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(
|
mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(
|
||||||
KeychainIntentService.EXTRA_MESSENGER);
|
KeychainIntentService.EXTRA_MESSENGER);
|
||||||
|
mPassthroughMessenger = null; // TODO remove, development hack
|
||||||
|
|
||||||
// preselect certify key id if given
|
// preselect certify key id if given
|
||||||
long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
|
long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
|
||||||
@ -143,9 +138,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
View view = inflater.inflate(R.layout.certify_key_fragment, null);
|
||||||
|
|
||||||
View view = inflater.inflate(R.layout.certify_key_fragment, getContainer());
|
|
||||||
|
|
||||||
mCertifyKeySpinner = (CertifyKeySpinner) view.findViewById(R.id.certify_key_spinner);
|
mCertifyKeySpinner = (CertifyKeySpinner) view.findViewById(R.id.certify_key_spinner);
|
||||||
mUploadKeyCheckbox = (CheckBox) view.findViewById(R.id.sign_key_upload_checkbox);
|
mUploadKeyCheckbox = (CheckBox) view.findViewById(R.id.sign_key_upload_checkbox);
|
||||||
@ -173,7 +166,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
Notify.showNotify(getActivity(), getString(R.string.select_key_to_certify),
|
Notify.showNotify(getActivity(), getString(R.string.select_key_to_certify),
|
||||||
Notify.Style.ERROR);
|
Notify.Style.ERROR);
|
||||||
} else {
|
} else {
|
||||||
initiateCertifying();
|
cryptoOperation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -183,7 +176,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
mUploadKeyCheckbox.setChecked(false);
|
mUploadKeyCheckbox.setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -307,7 +300,6 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
mUserIdsAdapter.swapCursor(matrix);
|
mUserIdsAdapter.swapCursor(matrix);
|
||||||
setContentShown(true, isResumed());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -315,50 +307,17 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
mUserIdsAdapter.swapCursor(null);
|
mUserIdsAdapter.swapCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected void cryptoOperation() {
|
||||||
* handles the UI bits of the signing process on the UI thread
|
cryptoOperation((CryptoInputParcel) null);
|
||||||
*/
|
|
||||||
private void initiateCertifying() {
|
|
||||||
// get the user's passphrase for this key (if required)
|
|
||||||
String passphrase;
|
|
||||||
try {
|
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId);
|
|
||||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
|
||||||
Log.e(Constants.TAG, "Key not found!", e);
|
|
||||||
getActivity().finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (passphrase == null) {
|
|
||||||
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
|
|
||||||
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mSignMasterKeyId);
|
|
||||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
|
||||||
// bail out; need to wait until the user has entered the passphrase before trying again
|
|
||||||
} else {
|
|
||||||
startCertifying();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void cryptoOperation(String passphrase) {
|
||||||
switch (requestCode) {
|
cryptoOperation((CryptoInputParcel) null);
|
||||||
case REQUEST_CODE_PASSPHRASE: {
|
|
||||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
|
||||||
String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
|
||||||
startCertifying();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
@Override
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* kicks off the actual signing process on a background thread
|
|
||||||
*/
|
|
||||||
private void startCertifying() {
|
|
||||||
// Bail out if there is not at least one user id selected
|
// Bail out if there is not at least one user id selected
|
||||||
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
|
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
|
||||||
if (certifyActions.isEmpty()) {
|
if (certifyActions.isEmpty()) {
|
||||||
@ -370,7 +329,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
{
|
{
|
||||||
// fill values for this action
|
// fill values for this action
|
||||||
CertifyActionsParcel parcel = new CertifyActionsParcel(new Date(), mSignMasterKeyId);
|
CertifyActionsParcel parcel = new CertifyActionsParcel(cryptoInput, mSignMasterKeyId);
|
||||||
parcel.mCertifyActions.addAll(certifyActions);
|
parcel.mCertifyActions.addAll(certifyActions);
|
||||||
|
|
||||||
data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
|
data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
|
||||||
@ -390,14 +349,21 @@ public class CertifyKeyFragment extends LoaderFragment
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Message is received after signing is done in KeychainIntentService
|
// Message is received after signing is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) {
|
getActivity(), getString(R.string.progress_certifying),
|
||||||
|
ProgressDialog.STYLE_SPINNER, true) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard KeychainIntentServiceHandler first
|
// handle messages by KeychainIntentCryptoServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
|
// handle pending messages
|
||||||
|
if (handlePendingMessage(message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||||
Bundle data = message.getData();
|
Bundle data = message.getData();
|
||||||
|
|
||||||
CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
|
CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
|
||||||
|
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class CryptoOperationFragment extends Fragment {
|
||||||
|
|
||||||
|
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
||||||
|
public static final int REQUEST_CODE_NFC = 0x00008002;
|
||||||
|
|
||||||
|
private void startPassphraseDialog(long subkeyId) {
|
||||||
|
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
|
||||||
|
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId);
|
||||||
|
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initiateNfcInput(NfcOperationsParcel nfcOps) {
|
||||||
|
Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
|
||||||
|
intent.putExtra(NfcOperationActivity.EXTRA_PIN, "123456");
|
||||||
|
intent.putExtra(NfcOperationActivity.EXTRA_NFC_OPS, nfcOps);
|
||||||
|
startActivityForResult(intent, REQUEST_CODE_NFC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CODE_PASSPHRASE: {
|
||||||
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||||
|
cryptoOperation(passphrase);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case REQUEST_CODE_NFC: {
|
||||||
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
CryptoInputParcel cryptoInput =
|
||||||
|
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
|
||||||
|
cryptoOperation(cryptoInput);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handlePendingMessage(Message message) {
|
||||||
|
|
||||||
|
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||||
|
Bundle data = message.getData();
|
||||||
|
|
||||||
|
InputPendingResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
|
||||||
|
|
||||||
|
if (result != null && result.isPending()) {
|
||||||
|
if (result.isPassphrasePending()) {
|
||||||
|
startPassphraseDialog(result.getPassphraseKeyId());
|
||||||
|
return true;
|
||||||
|
} else if (result.isNfcPending()) {
|
||||||
|
NfcOperationsParcel requiredInput = result.getNfcOperationsParcel();
|
||||||
|
initiateNfcInput(requiredInput);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unhandled pending result!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void cryptoOperation(CryptoInputParcel cryptoInput);
|
||||||
|
protected abstract void cryptoOperation(String passphrase);
|
||||||
|
|
||||||
|
}
|
@ -182,17 +182,17 @@ public class NfcOperationActivity extends BaseActivity {
|
|||||||
|
|
||||||
case NFC_DECRYPT:
|
case NFC_DECRYPT:
|
||||||
|
|
||||||
for (int i = 0; i < mNfcOperations.mInputHash.length; i++) {
|
for (int i = 0; i < mNfcOperations.mInputHashes.length; i++) {
|
||||||
byte[] hash = mNfcOperations.mInputHash[i];
|
byte[] hash = mNfcOperations.mInputHashes[i];
|
||||||
byte[] decryptedSessionKey = nfcDecryptSessionKey(hash);
|
byte[] decryptedSessionKey = nfcDecryptSessionKey(hash);
|
||||||
resultData.addCryptoData(hash, decryptedSessionKey);
|
resultData.addCryptoData(hash, decryptedSessionKey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NFC_SIGN:
|
case NFC_SIGN:
|
||||||
for (int i = 0; i < mNfcOperations.mInputHash.length; i++) {
|
for (int i = 0; i < mNfcOperations.mInputHashes.length; i++) {
|
||||||
byte[] hash = mNfcOperations.mInputHash[i];
|
byte[] hash = mNfcOperations.mInputHashes[i];
|
||||||
int algo = mNfcOperations.mSignAlgo[i];
|
int algo = mNfcOperations.mSignAlgos[i];
|
||||||
byte[] signedHash = nfcCalculateSignature(hash, algo);
|
byte[] signedHash = nfcCalculateSignature(hash, algo);
|
||||||
resultData.addCryptoData(hash, signedHash);
|
resultData.addCryptoData(hash, signedHash);
|
||||||
}
|
}
|
||||||
|
@ -1099,9 +1099,9 @@
|
|||||||
<string name="msg_crt_error_master_not_found">"Master key not found!"</string>
|
<string name="msg_crt_error_master_not_found">"Master key not found!"</string>
|
||||||
<string name="msg_crt_error_nothing">"No keys certified!"</string>
|
<string name="msg_crt_error_nothing">"No keys certified!"</string>
|
||||||
<string name="msg_crt_error_unlock">"Error unlocking master key!"</string>
|
<string name="msg_crt_error_unlock">"Error unlocking master key!"</string>
|
||||||
<string name="msg_crt_error_divert">"Certification with NFC is not (yet) supported!"</string>
|
|
||||||
<string name="msg_crt">"Certifying keyrings"</string>
|
<string name="msg_crt">"Certifying keyrings"</string>
|
||||||
<string name="msg_crt_master_fetch">"Fetching certifying master key"</string>
|
<string name="msg_crt_master_fetch">"Fetching certifying master key"</string>
|
||||||
|
<string name="msg_crt_nfc_return">"Returning for NFC input"</string>
|
||||||
<string name="msg_crt_save">"Saving certified key %s"</string>
|
<string name="msg_crt_save">"Saving certified key %s"</string>
|
||||||
<string name="msg_crt_saving">"Saving keyrings"</string>
|
<string name="msg_crt_saving">"Saving keyrings"</string>
|
||||||
<string name="msg_crt_unlock">"Unlocking master key"</string>
|
<string name="msg_crt_unlock">"Unlocking master key"</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user