mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -05:00
support yubikeys in (some) edit key operations
This commit is contained in:
parent
25d89b5550
commit
3b04636f5d
@ -83,7 +83,7 @@ public class CertifyOperation extends BaseOperation {
|
||||
}
|
||||
|
||||
// certification is always with the master key id, so use that one
|
||||
String passphrase = parcel.mCryptoInput.getPassphrase();
|
||||
char[] passphrase = parcel.mCryptoInput.getPassphrase();
|
||||
|
||||
if (!certificationKey.unlock(passphrase)) {
|
||||
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
|
||||
@ -103,7 +103,7 @@ public class CertifyOperation extends BaseOperation {
|
||||
|
||||
int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0;
|
||||
|
||||
NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(parcel.getSignatureTime());
|
||||
NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(parcel.mCryptoInput.getSignatureTime());
|
||||
|
||||
// Work through all requested certifications
|
||||
for (CertifyAction action : parcel.mCertifyActions) {
|
||||
@ -127,7 +127,7 @@ public class CertifyOperation extends BaseOperation {
|
||||
|
||||
PgpCertifyOperation op = new PgpCertifyOperation();
|
||||
PgpCertifyResult result = op.certify(certificationKey, publicRing,
|
||||
log, 2, action, parcel.getSignatureData(), parcel.getSignatureTime());
|
||||
log, 2, action, parcel.getSignatureData(), parcel.mCryptoInput.getSignatureTime());
|
||||
|
||||
if (!result.success()) {
|
||||
certifyError += 1;
|
||||
|
@ -21,6 +21,7 @@ import android.content.Context;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
|
||||
@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
@ -55,7 +57,7 @@ public class EditKeyOperation extends BaseOperation {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) {
|
||||
public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_ED, 0);
|
||||
@ -69,7 +71,7 @@ public class EditKeyOperation extends BaseOperation {
|
||||
PgpEditKeyResult modifyResult;
|
||||
{
|
||||
PgpKeyOperation keyOperations =
|
||||
new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled);
|
||||
new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled, cryptoInput);
|
||||
|
||||
// If a key id is specified, fetch and edit
|
||||
if (saveParcel.mMasterKeyId != null) {
|
||||
@ -80,7 +82,10 @@ public class EditKeyOperation extends BaseOperation {
|
||||
CanonicalizedSecretKeyRing secRing =
|
||||
mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
|
||||
|
||||
modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
|
||||
modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel);
|
||||
if (modifyResult.isPending()) {
|
||||
return modifyResult;
|
||||
}
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.add(LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2);
|
||||
|
@ -11,66 +11,35 @@ 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 RequiredInputParcel mRequiredInput;
|
||||
final Long mKeyIdPassphraseNeeded;
|
||||
|
||||
public InputPendingResult(int result, OperationLog log) {
|
||||
super(result, log);
|
||||
mRequiredInput = null;
|
||||
mKeyIdPassphraseNeeded = null;
|
||||
}
|
||||
|
||||
public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput) {
|
||||
super(RESULT_PENDING_NFC, log);
|
||||
super(RESULT_PENDING, 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 RequiredInputParcel getRequiredInputParcel() {
|
||||
return mRequiredInput;
|
||||
}
|
||||
|
||||
public long getPassphraseKeyId() {
|
||||
return mKeyIdPassphraseNeeded;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -512,6 +512,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
|
||||
// secret key modify
|
||||
MSG_MF (LogLevel.START, R.string.msg_mr),
|
||||
MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert),
|
||||
MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial),
|
||||
MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode),
|
||||
MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint),
|
||||
@ -529,6 +530,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
|
||||
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
|
||||
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
|
||||
MSG_MF_INPUT_REQUIRED (LogLevel.OK, R.string.msg_mf_input_required),
|
||||
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
|
||||
MSG_MF_NOTATION_PIN (LogLevel.DEBUG, R.string.msg_mf_notation_pin),
|
||||
MSG_MF_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_mf_notation_empty),
|
||||
@ -596,7 +598,6 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success),
|
||||
|
||||
// messages used in UI code
|
||||
MSG_EK_ERROR_DIVERT (LogLevel.ERROR, R.string.msg_ek_error_divert),
|
||||
MSG_EK_ERROR_DUMMY (LogLevel.ERROR, R.string.msg_ek_error_dummy),
|
||||
MSG_EK_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_ek_error_not_found),
|
||||
|
||||
|
@ -22,8 +22,10 @@ import android.os.Parcel;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
||||
public class PgpEditKeyResult extends OperationResult {
|
||||
|
||||
public class PgpEditKeyResult extends InputPendingResult {
|
||||
|
||||
private transient UncachedKeyRing mRing;
|
||||
public final long mRingMasterKeyId;
|
||||
@ -35,6 +37,11 @@ public class PgpEditKeyResult extends OperationResult {
|
||||
mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
|
||||
}
|
||||
|
||||
public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput) {
|
||||
super(log, requiredInput);
|
||||
mRingMasterKeyId = Constants.key.none;
|
||||
}
|
||||
|
||||
public UncachedKeyRing getRing() {
|
||||
return mRing;
|
||||
}
|
||||
|
@ -149,10 +149,14 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
|
||||
}
|
||||
|
||||
public boolean unlock(String passphrase) throws PgpGeneralException {
|
||||
return unlock(passphrase.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true on right passphrase
|
||||
*/
|
||||
public boolean unlock(String passphrase) throws PgpGeneralException {
|
||||
public boolean unlock(char[] passphrase) throws PgpGeneralException {
|
||||
// handle keys on OpenPGP cards like they were unlocked
|
||||
if (mSecretKey.getS2K() != null
|
||||
&& mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
|
||||
@ -164,7 +168,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
// try to extract keys using the passphrase
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase);
|
||||
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
|
||||
mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED;
|
||||
} catch (PGPException e) {
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.bcpg.sig.Features;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.jce.spec.ElGamalParameterSpec;
|
||||
@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
import org.spongycastle.openpgp.operator.PGPDataDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
||||
@ -43,6 +44,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBu
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
@ -54,6 +57,9 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -85,9 +91,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* This indicator may be null.
|
||||
*/
|
||||
public class PgpKeyOperation {
|
||||
|
||||
private Stack<Progressable> mProgress;
|
||||
private AtomicBoolean mCancelled;
|
||||
|
||||
NfcSignOperationsBuilder mNfcSignOps;
|
||||
private CryptoInputParcel mCryptoInput;
|
||||
|
||||
public PgpKeyOperation(Progressable progress) {
|
||||
super();
|
||||
if (progress != null) {
|
||||
@ -96,9 +106,11 @@ public class PgpKeyOperation {
|
||||
}
|
||||
}
|
||||
|
||||
public PgpKeyOperation(Progressable progress, AtomicBoolean cancelled) {
|
||||
public PgpKeyOperation(Progressable progress, AtomicBoolean cancelled, CryptoInputParcel cryptoInput) {
|
||||
this(progress);
|
||||
mCancelled = cancelled;
|
||||
mNfcSignOps = new NfcSignOperationsBuilder(cryptoInput.getSignatureTime());
|
||||
mCryptoInput = cryptoInput;
|
||||
}
|
||||
|
||||
private boolean checkCancelled() {
|
||||
@ -316,7 +328,8 @@ public class PgpKeyOperation {
|
||||
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
|
||||
|
||||
subProgressPush(50, 100);
|
||||
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
|
||||
CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), "");
|
||||
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, log);
|
||||
|
||||
} catch (PGPException e) {
|
||||
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
|
||||
@ -347,8 +360,8 @@ public class PgpKeyOperation {
|
||||
* namely stripping of subkeys and changing the protection mode of dummy keys.
|
||||
*
|
||||
*/
|
||||
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
|
||||
String passphrase) {
|
||||
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR,
|
||||
SaveKeyringParcel saveParcel) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
int indent = 0;
|
||||
@ -386,11 +399,16 @@ public class PgpKeyOperation {
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
// If we have no passphrase, only allow restricted operation
|
||||
if (passphrase == null) {
|
||||
if (saveParcel.isRestrictedOnly()) {
|
||||
return internalRestricted(sKR, saveParcel, log);
|
||||
}
|
||||
|
||||
// Do we require a passphrase? If so, pass it along
|
||||
if (!isDivertToCard(masterSecretKey) && !mCryptoInput.hasPassphrase()) {
|
||||
return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredPassphrase(
|
||||
masterSecretKey.getKeyID(), mCryptoInput.getSignatureTime()));
|
||||
}
|
||||
|
||||
// read masterKeyFlags, and use the same as before.
|
||||
// since this is the master key, this contains at least CERTIFY_OTHER
|
||||
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
|
||||
@ -398,13 +416,13 @@ public class PgpKeyOperation {
|
||||
Date expiryTime = wsKR.getPublicKey().getExpiryTime();
|
||||
long masterKeyExpiry = expiryTime != null ? expiryTime.getTime() / 1000 : 0L;
|
||||
|
||||
return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log);
|
||||
return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, log);
|
||||
|
||||
}
|
||||
|
||||
private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
|
||||
int masterKeyFlags, long masterKeyExpiry,
|
||||
SaveKeyringParcel saveParcel, String passphrase,
|
||||
SaveKeyringParcel saveParcel,
|
||||
OperationLog log) {
|
||||
|
||||
int indent = 1;
|
||||
@ -413,20 +431,27 @@ public class PgpKeyOperation {
|
||||
|
||||
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
|
||||
|
||||
PGPPrivateKey masterPrivateKey;
|
||||
|
||||
if (isDivertToCard(masterSecretKey)) {
|
||||
masterPrivateKey = null;
|
||||
log.add(LogType.MSG_MF_DIVERT, indent);
|
||||
} else {
|
||||
|
||||
// 1. Unlock private key
|
||||
progress(R.string.progress_modify_unlock, 10);
|
||||
log.add(LogType.MSG_MF_UNLOCK, indent);
|
||||
PGPPrivateKey masterPrivateKey;
|
||||
{
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mCryptoInput.getPassphrase());
|
||||
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
|
||||
} catch (PGPException e) {
|
||||
log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@ -479,9 +504,16 @@ public class PgpKeyOperation {
|
||||
boolean isPrimary = saveParcel.mChangePrimaryUserId != null
|
||||
&& userId.equals(saveParcel.mChangePrimaryUserId);
|
||||
// generate and add new certificate
|
||||
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
|
||||
masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry);
|
||||
try {
|
||||
PGPSignature cert = generateUserIdSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, userId,
|
||||
isPrimary, masterKeyFlags, masterKeyExpiry);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
@ -508,9 +540,15 @@ public class PgpKeyOperation {
|
||||
PGPUserAttributeSubpacketVector vector = attribute.getVector();
|
||||
|
||||
// generate and add new certificate
|
||||
PGPSignature cert = generateUserAttributeSignature(masterPrivateKey,
|
||||
masterPublicKey, vector);
|
||||
try {
|
||||
PGPSignature cert = generateUserAttributeSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, vector);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
@ -538,9 +576,15 @@ public class PgpKeyOperation {
|
||||
|
||||
// a duplicate revocation will be removed during canonicalization, so no need to
|
||||
// take care of that here.
|
||||
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
|
||||
masterPublicKey, userId);
|
||||
try {
|
||||
PGPSignature cert = generateRevocationSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, userId);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
@ -610,11 +654,18 @@ public class PgpKeyOperation {
|
||||
log.add(LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
|
||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||
modifiedPublicKey, userId, currentCert);
|
||||
try {
|
||||
PGPSignature newCert = generateUserIdSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, userId, false,
|
||||
masterKeyFlags, masterKeyExpiry);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||
modifiedPublicKey, userId, newCert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -626,11 +677,17 @@ public class PgpKeyOperation {
|
||||
log.add(LogType.MSG_MF_PRIMARY_NEW, indent);
|
||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||
modifiedPublicKey, userId, currentCert);
|
||||
try {
|
||||
PGPSignature newCert = generateUserIdSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, userId, true,
|
||||
masterKeyFlags, masterKeyExpiry);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||
modifiedPublicKey, userId, newCert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@ -717,7 +774,8 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
PGPPublicKey pKey =
|
||||
updateMasterCertificates(masterPrivateKey, masterPublicKey,
|
||||
updateMasterCertificates(
|
||||
masterSecretKey, masterPrivateKey, masterPublicKey,
|
||||
flags, expiry, indent, log);
|
||||
if (pKey == null) {
|
||||
// error log entry has already been added by updateMasterCertificates itself
|
||||
@ -755,9 +813,16 @@ public class PgpKeyOperation {
|
||||
pKey = PGPPublicKey.removeCertification(pKey, sig);
|
||||
}
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
mCryptoInput.getPassphrase());
|
||||
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
||||
PGPSignature sig = generateSubkeyBindingSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry);
|
||||
|
||||
// generate and add new signature
|
||||
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
|
||||
sKey, pKey, flags, expiry, passphrase);
|
||||
pKey = PGPPublicKey.addCertification(pKey, sig);
|
||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
||||
}
|
||||
@ -781,10 +846,17 @@ public class PgpKeyOperation {
|
||||
PGPPublicKey pKey = sKey.getPublicKey();
|
||||
|
||||
// generate and add new signature
|
||||
PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey);
|
||||
try {
|
||||
PGPSignature sig = generateRevocationSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPublicKey, masterPrivateKey, pKey);
|
||||
|
||||
pKey = PGPPublicKey.addCertification(pKey, sig);
|
||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
@ -827,19 +899,29 @@ public class PgpKeyOperation {
|
||||
|
||||
// add subkey binding signature (making this a sub rather than master key)
|
||||
PGPPublicKey pKey = keyPair.getPublicKey();
|
||||
try {
|
||||
PGPSignature cert = generateSubkeyBindingSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
|
||||
add.mFlags, add.mExpiry);
|
||||
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
|
||||
PGPSecretKey sKey; {
|
||||
char[] passphrase = mCryptoInput.getPassphrase();
|
||||
if (passphrase == null) {
|
||||
passphrase = new char[] { };
|
||||
}
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase);
|
||||
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
@ -867,7 +949,7 @@ public class PgpKeyOperation {
|
||||
indent += 1;
|
||||
|
||||
sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
|
||||
passphrase, saveParcel.mNewUnlock, log, indent);
|
||||
mCryptoInput.getPassphrase(), saveParcel.mNewUnlock, log, indent);
|
||||
if (sKR == null) {
|
||||
// The error has been logged above, just return a bad state
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
@ -891,6 +973,12 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
progress(R.string.progress_done, 100);
|
||||
|
||||
if (!mNfcSignOps.isEmpty()) {
|
||||
log.add(LogType.MSG_MF_INPUT_REQUIRED, indent);
|
||||
return new PgpEditKeyResult(log, mNfcSignOps.build());
|
||||
}
|
||||
|
||||
log.add(LogType.MSG_MF_SUCCESS, indent);
|
||||
return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
|
||||
|
||||
@ -967,7 +1055,7 @@ public class PgpKeyOperation {
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
PGPPrivateKey masterPrivateKey,
|
||||
String passphrase,
|
||||
char[] passphrase,
|
||||
ChangeUnlockParcel newUnlock,
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
@ -1051,14 +1139,14 @@ public class PgpKeyOperation {
|
||||
private static PGPSecretKeyRing applyNewPassphrase(
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
String passphrase,
|
||||
char[] passphrase,
|
||||
String newPassphrase,
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
|
||||
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase);
|
||||
// Build key encryptor based on new passphrase
|
||||
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
@ -1115,8 +1203,9 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
/** Update all (non-revoked) uid signatures with new flags and expiry time. */
|
||||
private static PGPPublicKey updateMasterCertificates(
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey,
|
||||
private PGPPublicKey updateMasterCertificates(
|
||||
PGPSecretKey masterSecretKey, PGPPrivateKey masterPrivateKey,
|
||||
PGPPublicKey masterPublicKey,
|
||||
int flags, long expiry, int indent, OperationLog log)
|
||||
throws PGPException, IOException, SignatureException {
|
||||
|
||||
@ -1172,10 +1261,16 @@ public class PgpKeyOperation {
|
||||
currentCert.getHashedSubPackets().isPrimaryUserID();
|
||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||
modifiedPublicKey, userId, currentCert);
|
||||
try {
|
||||
PGPSignature newCert = generateUserIdSignature(
|
||||
getSignatureGenerator(masterSecretKey, mCryptoInput),
|
||||
mCryptoInput.getSignatureTime(),
|
||||
masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||
modifiedPublicKey, userId, newCert);
|
||||
} catch (NfcInteractionNeeded e) {
|
||||
mNfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||
}
|
||||
ok = true;
|
||||
|
||||
}
|
||||
@ -1190,15 +1285,37 @@ public class PgpKeyOperation {
|
||||
|
||||
}
|
||||
|
||||
private static PGPSignature generateUserIdSignature(
|
||||
private static PGPSignatureGenerator getSignatureGenerator(
|
||||
PGPSecretKey secretKey, CryptoInputParcel cryptoInput) {
|
||||
|
||||
PGPContentSignerBuilder builder;
|
||||
|
||||
S2K s2k = secretKey.getS2K();
|
||||
if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
|
||||
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
|
||||
// use synchronous "NFC based" SignerBuilder
|
||||
builder = new NfcSyncPGPContentSignerBuilder(
|
||||
secretKey.getPublicKey().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
|
||||
secretKey.getKeyID(), cryptoInput.getCryptoData())
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
} else {
|
||||
// content signer based on signing key algorithm and chosen hash algorithm
|
||||
builder = new JcaPGPContentSignerBuilder(
|
||||
secretKey.getPublicKey().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
}
|
||||
|
||||
return new PGPSignatureGenerator(builder);
|
||||
|
||||
}
|
||||
|
||||
private PGPSignature generateUserIdSignature(
|
||||
PGPSignatureGenerator sGen, Date creationTime,
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
|
||||
int flags, long expiry)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
{
|
||||
@ -1222,7 +1339,7 @@ public class PgpKeyOperation {
|
||||
hashedPacketsGen.setPrimaryUserID(false, primary);
|
||||
|
||||
/* critical subpackets: we consider those important for a modern pgp implementation */
|
||||
hashedPacketsGen.setSignatureCreationTime(true, new Date());
|
||||
hashedPacketsGen.setSignatureCreationTime(true, creationTime);
|
||||
// Request that senders add the MDC to the message (authenticate unsigned messages)
|
||||
hashedPacketsGen.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION);
|
||||
hashedPacketsGen.setKeyFlags(true, flags);
|
||||
@ -1238,19 +1355,15 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
private static PGPSignature generateUserAttributeSignature(
|
||||
PGPSignatureGenerator sGen, Date creationTime,
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey,
|
||||
PGPUserAttributeSubpacketVector vector)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
{
|
||||
/* critical subpackets: we consider those important for a modern pgp implementation */
|
||||
hashedPacketsGen.setSignatureCreationTime(true, new Date());
|
||||
hashedPacketsGen.setSignatureCreationTime(true, creationTime);
|
||||
}
|
||||
|
||||
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||
@ -1259,29 +1372,24 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
private static PGPSignature generateRevocationSignature(
|
||||
PGPSignatureGenerator sGen, Date creationTime,
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
|
||||
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
|
||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
subHashedPacketsGen.setSignatureCreationTime(true, new Date());
|
||||
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
|
||||
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
|
||||
return sGen.generateCertification(userId, pKey);
|
||||
}
|
||||
|
||||
private static PGPSignature generateRevocationSignature(
|
||||
PGPSignatureGenerator sGen, Date creationTime,
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
subHashedPacketsGen.setSignatureCreationTime(true, new Date());
|
||||
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
|
||||
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||
// Generate key revocation or subkey revocation, depending on master/subkey-ness
|
||||
if (masterPublicKey.getKeyID() == pKey.getKeyID()) {
|
||||
@ -1293,26 +1401,12 @@ public class PgpKeyOperation {
|
||||
}
|
||||
}
|
||||
|
||||
private static PGPSignature generateSubkeyBindingSignature(
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
passphrase.toCharArray());
|
||||
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
||||
return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey,
|
||||
pKey, flags, expiry);
|
||||
}
|
||||
|
||||
static PGPSignature generateSubkeyBindingSignature(
|
||||
PGPSignatureGenerator sGen, Date creationTime,
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||
PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
|
||||
// date for signing
|
||||
Date creationTime = new Date();
|
||||
|
||||
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
|
||||
// If this key can sign, we need a primary key binding signature
|
||||
@ -1323,10 +1417,10 @@ public class PgpKeyOperation {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||
PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
|
||||
PGPSignatureGenerator subSigGen = new PGPSignatureGenerator(signerBuilder);
|
||||
subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||
subSigGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||
PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey);
|
||||
unhashedPacketsGen.setEmbeddedSignature(true, certification);
|
||||
}
|
||||
|
||||
@ -1341,10 +1435,6 @@ public class PgpKeyOperation {
|
||||
}
|
||||
}
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
|
||||
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||
sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
|
||||
@ -1371,4 +1461,10 @@ public class PgpKeyOperation {
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static boolean isDivertToCard(PGPSecretKey secretKey) {
|
||||
S2K s2k = secretKey.getS2K();
|
||||
return s2k.getType() == S2K.GNU_DUMMY_S2K
|
||||
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -76,10 +76,6 @@ public class CertifyActionsParcel implements Parcelable {
|
||||
return mCryptoInput.getCryptoData();
|
||||
}
|
||||
|
||||
public Date getSignatureTime() {
|
||||
return mCryptoInput.getSignatureTime();
|
||||
}
|
||||
|
||||
public static final Creator<CertifyActionsParcel> CREATOR = new Creator<CertifyActionsParcel>() {
|
||||
public CertifyActionsParcel createFromParcel(final Parcel source) {
|
||||
return new CertifyActionsParcel(source);
|
||||
|
@ -61,6 +61,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -160,6 +161,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
||||
// save keyring
|
||||
public static final String EDIT_KEYRING_PARCEL = "save_parcel";
|
||||
public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
|
||||
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
|
||||
|
||||
// delete keyring(s)
|
||||
public static final String DELETE_KEY_LIST = "delete_list";
|
||||
@ -469,11 +471,11 @@ public class KeychainIntentService extends IntentService implements Progressable
|
||||
|
||||
// Input
|
||||
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
|
||||
String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
|
||||
CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
|
||||
|
||||
// Operation
|
||||
EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
|
||||
EditKeyResult result = op.execute(saveParcel, passphrase);
|
||||
OperationResult result = op.execute(saveParcel, cryptoInput);
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
@ -81,8 +81,8 @@ public class CryptoInputParcel implements Parcelable {
|
||||
return mPassphrase != null;
|
||||
}
|
||||
|
||||
public String getPassphrase() {
|
||||
return mPassphrase;
|
||||
public char[] getPassphrase() {
|
||||
return mPassphrase == null ? null : mPassphrase.toCharArray();
|
||||
}
|
||||
|
||||
public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() {
|
||||
@ -95,4 +95,19 @@ public class CryptoInputParcel implements Parcelable {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("CryptoInput: { ");
|
||||
b.append(mSignatureTime).append(" ");
|
||||
if (mPassphrase != null) {
|
||||
b.append("passphrase");
|
||||
}
|
||||
if (mCryptoData != null) {
|
||||
b.append(mCryptoData.size());
|
||||
b.append(" hashes ");
|
||||
}
|
||||
b.append("}");
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,13 @@ public class RequiredInputParcel implements Parcelable {
|
||||
}
|
||||
|
||||
public Date mSignatureTime;
|
||||
|
||||
public final RequiredInputType mType;
|
||||
|
||||
public String mNfcPin = "123456";
|
||||
public final byte[][] mInputHashes;
|
||||
public final int[] mSignAlgos;
|
||||
|
||||
private Long mSubKeyId;
|
||||
|
||||
private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes,
|
||||
|
@ -9,6 +9,7 @@ import android.support.v4.app.Fragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
@ -25,7 +26,6 @@ public abstract class CryptoOperationFragment extends Fragment {
|
||||
case NFC_DECRYPT:
|
||||
case NFC_SIGN: {
|
||||
Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_PIN, "123456");
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
|
||||
startActivityForResult(intent, REQUEST_CODE_NFC);
|
||||
return;
|
||||
@ -76,10 +76,14 @@ public abstract class CryptoOperationFragment extends Fragment {
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
Bundle data = message.getData();
|
||||
|
||||
InputPendingResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
|
||||
OperationResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
|
||||
if (result == null || ! (result instanceof InputPendingResult)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != null && result.isPending()) {
|
||||
RequiredInputParcel requiredInput = result.getRequiredInputParcel();
|
||||
InputPendingResult pendingResult = (InputPendingResult) result;
|
||||
if (pendingResult.isPending()) {
|
||||
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
|
||||
initiateInputActivity(requiredInput);
|
||||
return true;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
@ -51,10 +53,10 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
|
||||
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.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
|
||||
@ -68,14 +70,12 @@ import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class EditKeyFragment extends LoaderFragment implements
|
||||
public class EditKeyFragment extends CryptoOperationFragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
public static final String ARG_DATA_URI = "uri";
|
||||
public static final String ARG_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
|
||||
|
||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
||||
|
||||
private ListView mUserIdsList;
|
||||
private ListView mSubkeysList;
|
||||
private ListView mUserIdsAddedList;
|
||||
@ -100,7 +100,6 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
private SaveKeyringParcel mSaveKeyringParcel;
|
||||
|
||||
private String mPrimaryUserId;
|
||||
private String mCurrentPassphrase;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
@ -129,8 +128,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.edit_key_fragment, getContainer());
|
||||
View view = inflater.inflate(R.layout.edit_key_fragment, null);
|
||||
|
||||
mUserIdsList = (ListView) view.findViewById(R.id.edit_key_user_ids);
|
||||
mSubkeysList = (ListView) view.findViewById(R.id.edit_key_keys);
|
||||
@ -140,7 +138,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
mAddUserId = view.findViewById(R.id.edit_key_action_add_user_id);
|
||||
mAddSubkey = view.findViewById(R.id.edit_key_action_add_key);
|
||||
|
||||
return root;
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,7 +153,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
if (mDataUri == null) {
|
||||
returnKeyringParcel();
|
||||
} else {
|
||||
saveInDatabase(mCurrentPassphrase);
|
||||
cryptoOperation(new CryptoInputParcel(new Date()));
|
||||
}
|
||||
}
|
||||
}, new OnClickListener() {
|
||||
@ -185,18 +183,12 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) {
|
||||
mSaveKeyringParcel = saveKeyringParcel;
|
||||
mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId;
|
||||
if (saveKeyringParcel.mNewUnlock != null) {
|
||||
mCurrentPassphrase = saveKeyringParcel.mNewUnlock.mNewPassphrase;
|
||||
}
|
||||
|
||||
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, true);
|
||||
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
|
||||
|
||||
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys, true);
|
||||
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
|
||||
|
||||
// show directly
|
||||
setContentShown(true);
|
||||
}
|
||||
|
||||
private void loadData(Uri dataUri) {
|
||||
@ -216,9 +208,6 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
case GNU_DUMMY:
|
||||
finishWithError(LogType.MSG_EK_ERROR_DUMMY);
|
||||
return;
|
||||
case DIVERT_TO_CARD:
|
||||
finishWithError(LogType.MSG_EK_ERROR_DIVERT);
|
||||
break;
|
||||
}
|
||||
|
||||
mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint());
|
||||
@ -229,24 +218,10 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(),
|
||||
mSaveKeyringParcel.mMasterKeyId, mSaveKeyringParcel.mMasterKeyId);
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentPassphrase == null) {
|
||||
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
|
||||
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mSaveKeyringParcel.mMasterKeyId);
|
||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||
} else {
|
||||
// Prepare the loaders. Either re-connect with an existing ones,
|
||||
// or start new ones.
|
||||
getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this);
|
||||
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this);
|
||||
}
|
||||
|
||||
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, mSaveKeyringParcel);
|
||||
mUserIdsList.setAdapter(mUserIdsAdapter);
|
||||
@ -262,28 +237,6 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
// Prepare the loaders. Either re-connect with an existing ones,
|
||||
// or start new ones.
|
||||
getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this);
|
||||
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this);
|
||||
} else {
|
||||
getActivity().finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mChangePassphrase.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -322,7 +275,6 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
setContentShown(false);
|
||||
|
||||
switch (id) {
|
||||
case LOADER_ID_USER_IDS: {
|
||||
@ -355,7 +307,6 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
break;
|
||||
|
||||
}
|
||||
setContentShown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,7 +348,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
|
||||
SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance(
|
||||
messenger, mCurrentPassphrase, R.string.title_change_passphrase);
|
||||
messenger, R.string.title_change_passphrase);
|
||||
|
||||
setPassphraseDialog.show(getActivity().getSupportFragmentManager(), "setPassphraseDialog");
|
||||
}
|
||||
@ -593,8 +544,11 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
private void saveInDatabase(String passphrase) {
|
||||
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
|
||||
@Override
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
|
||||
|
||||
Log.d(Constants.TAG, "cryptoInput:\n" + cryptoInput);
|
||||
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel);
|
||||
|
||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||
getActivity(),
|
||||
@ -605,6 +559,10 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (handlePendingMessage(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
|
||||
// get returned data bundle
|
||||
@ -640,7 +598,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase);
|
||||
data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
|
||||
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
|
@ -62,10 +62,9 @@ public abstract class EncryptActivity extends BaseActivity {
|
||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||
}
|
||||
|
||||
protected void startNfcSign(long keyId, String pin, RequiredInputParcel nfcOps) {
|
||||
protected void startNfcSign(long keyId, RequiredInputParcel nfcOps) {
|
||||
|
||||
Intent intent = new Intent(this, NfcOperationActivity.class);
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_PIN, pin);
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, nfcOps);
|
||||
// TODO respect keyid(?)
|
||||
|
||||
@ -148,8 +147,7 @@ public abstract class EncryptActivity extends BaseActivity {
|
||||
pgpResult.getNfcHash(),
|
||||
pgpResult.getNfcAlgo(),
|
||||
input.getSignatureTime());
|
||||
startNfcSign(pgpResult.getNfcKeyId(),
|
||||
pgpResult.getNfcPassphrase(), parcel);
|
||||
startNfcSign(pgpResult.getNfcKeyId(), parcel);
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("Unhandled pending result!");
|
||||
|
@ -40,7 +40,6 @@ import java.nio.ByteBuffer;
|
||||
@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
|
||||
public class NfcOperationActivity extends BaseActivity {
|
||||
|
||||
public static final String EXTRA_PIN = "pin";
|
||||
public static final String EXTRA_REQUIRED_INPUT = "required_input";
|
||||
|
||||
public static final String RESULT_DATA = "result_data";
|
||||
@ -50,8 +49,6 @@ public class NfcOperationActivity extends BaseActivity {
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private IsoDep mIsoDep;
|
||||
|
||||
private String mPin;
|
||||
|
||||
RequiredInputParcel mNfcOperations;
|
||||
|
||||
@Override
|
||||
@ -70,7 +67,6 @@ public class NfcOperationActivity extends BaseActivity {
|
||||
Bundle data = intent.getExtras();
|
||||
|
||||
mNfcOperations = data.getParcelable(EXTRA_REQUIRED_INPUT);
|
||||
mPin = data.getString(EXTRA_PIN);
|
||||
|
||||
}
|
||||
|
||||
@ -161,14 +157,16 @@ public class NfcOperationActivity extends BaseActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
String pin = mNfcOperations.mNfcPin;
|
||||
|
||||
// Command APDU for VERIFY command (page 32)
|
||||
String login =
|
||||
"00" // CLA
|
||||
+ "20" // INS
|
||||
+ "00" // P1
|
||||
+ "82" // P2 (PW1)
|
||||
+ String.format("%02x", mPin.length()) // Lc
|
||||
+ Hex.toHexString(mPin.getBytes());
|
||||
+ String.format("%02x", pin.length()) // Lc
|
||||
+ Hex.toHexString(pin.getBytes());
|
||||
if ( ! card(login).equals(accepted)) { // login
|
||||
toast("Wrong PIN!");
|
||||
setResult(RESULT_CANCELED);
|
||||
|
@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
private static final String ARG_TITLE = "title";
|
||||
private static final String ARG_OLD_PASSPHRASE = "old_passphrase";
|
||||
|
||||
public static final int MESSAGE_OKAY = 1;
|
||||
|
||||
@ -67,12 +66,11 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
* @param messenger to communicate back after setting the passphrase
|
||||
* @return
|
||||
*/
|
||||
public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) {
|
||||
public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) {
|
||||
SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putParcelable(ARG_MESSENGER, messenger);
|
||||
args.putString(ARG_OLD_PASSPHRASE, oldPassphrase);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
@ -88,7 +86,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
|
||||
int title = getArguments().getInt(ARG_TITLE);
|
||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE);
|
||||
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
||||
|
||||
@ -102,13 +99,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again);
|
||||
mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase);
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(oldPassphrase)) {
|
||||
mNoPassphraseCheckBox.setChecked(true);
|
||||
mPassphraseEditText.setEnabled(false);
|
||||
mPassphraseAgainEditText.setEnabled(false);
|
||||
}
|
||||
|
||||
mNoPassphraseCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
|
@ -897,6 +897,7 @@
|
||||
|
||||
<!-- modifySecretKeyRing -->
|
||||
<string name="msg_mr">"Modifying keyring %s"</string>
|
||||
<string name="msg_mf_divert">"Will divert to card/nfc for crypto operations"</string>
|
||||
<string name="msg_mf_error_divert_serial">"The serial number of a divert-to-card key must be 16 bytes! This is a programming error, please file a bug report!"</string>
|
||||
<string name="msg_mf_error_encode">"Encoding exception!"</string>
|
||||
<string name="msg_mf_error_fingerprint">"Actual key fingerprint does not match the expected one!"</string>
|
||||
@ -911,6 +912,7 @@
|
||||
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
|
||||
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
|
||||
<string name="msg_mf_error_sig">"Signature exception!"</string>
|
||||
<string name="msg_mf_input_required">"Diverting to card/nfc for crypto operations"</string>
|
||||
<string name="msg_mf_master">"Modifying master certifications"</string>
|
||||
<string name="msg_mf_notation_empty">"Adding empty notation packet"</string>
|
||||
<string name="msg_mf_notation_pin">"Adding PIN notation packet"</string>
|
||||
@ -987,7 +989,6 @@
|
||||
<string name="msg_pr_success">"Key successfully promoted"</string>
|
||||
|
||||
<!-- Other messages used in OperationLogs -->
|
||||
<string name="msg_ek_error_divert">"Editing of NFC keys is not (yet) supported!"</string>
|
||||
<string name="msg_ek_error_dummy">"Cannot edit keyring with stripped master key!"</string>
|
||||
<string name="msg_ek_error_not_found">"Key not found!"</string>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user