add create key capabilities to SaveKeyringParcel

This commit is contained in:
Vincent Breitmoser 2014-06-29 22:34:53 +02:00
parent 6d7a9ec48a
commit de698b8955
7 changed files with 118 additions and 61 deletions

View File

@ -38,6 +38,7 @@ import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PGPDigestCalculator; import org.spongycastle.openpgp.operator.PGPDigestCalculator;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
@ -50,6 +51,8 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.Primes;
import java.io.IOException; import java.io.IOException;
@ -175,6 +178,40 @@ public class PgpKeyOperation {
} }
} }
public UncachedKeyRing createSecretKeyRing(SaveKeyringParcel saveParcel, OperationLog log,
int indent) {
try {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
indent += 1;
updateProgress(R.string.progress_building_key, 0, 100);
if (saveParcel.addSubKeys == null || saveParcel.addSubKeys.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent);
return null;
}
SubkeyAdd add = saveParcel.addSubKeys.remove(0);
PGPSecretKey masterSecretKey = createKey(add.mAlgorithm, add.mKeysize,
saveParcel.newPassphrase, true);
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
return internal(sKR, masterSecretKey, saveParcel, saveParcel.newPassphrase, log, indent);
} catch (PGPException e) {
Log.e(Constants.TAG, "pgp error encoding key", e);
return null;
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
return null;
} catch (PgpGeneralMsgIdException e) {
return null;
}
}
/** This method introduces a list of modifications specified by a SaveKeyringParcel to a /** This method introduces a list of modifications specified by a SaveKeyringParcel to a
* WrappedSecretKeyRing. * WrappedSecretKeyRing.
* *
@ -204,28 +241,49 @@ public class PgpKeyOperation {
indent += 1; indent += 1;
updateProgress(R.string.progress_building_key, 0, 100); updateProgress(R.string.progress_building_key, 0, 100);
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
return null;
}
// We work on bouncycastle object level here // We work on bouncycastle object level here
PGPSecretKeyRing sKR = wsKR.getRing(); PGPSecretKeyRing sKR = wsKR.getRing();
PGPPublicKey masterPublicKey = sKR.getPublicKey();
PGPSecretKey masterSecretKey = sKR.getSecretKey(); PGPSecretKey masterSecretKey = sKR.getSecretKey();
// Make sure the fingerprint matches
if (saveParcel.mFingerprint == null
|| !Arrays.equals(saveParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return null;
}
return internal(sKR, masterSecretKey, saveParcel, passphrase, log, indent);
}
private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
SaveKeyringParcel saveParcel, String passphrase,
OperationLog log, int indent) {
updateProgress(R.string.progress_certifying_master_key, 20, 100);
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// 1. Unlock private key // 1. Unlock private key
log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent); log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent);
PGPPrivateKey masterPrivateKey; { PGPPrivateKey masterPrivateKey;
{
try { try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) { } catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent+1); log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
return null; return null;
} }
} }
if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) {
return null;
}
updateProgress(R.string.progress_certifying_master_key, 20, 100);
// work on master secret key // work on master secret key
try { try {
@ -262,7 +320,7 @@ public class PgpKeyOperation {
} }
// 4a. For each subkey change, generate new subkey binding certificate // 4a. For each subkey change, generate new subkey binding certificate
for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE, log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));

View File

@ -8,16 +8,13 @@ import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchProviderException;
import java.util.Iterator; import java.util.Iterator;
public class WrappedSecretKeyRing extends WrappedKeyRing { public class WrappedSecretKeyRing extends WrappedKeyRing {
@ -91,29 +88,6 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
} }
} }
public UncachedKeyRing changeSecretKeyPassphrase(String oldPassphrase,
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
mRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return new UncachedKeyRing(newKeyRing);
}
public IterableIterator<WrappedSecretKey> secretKeyIterator() { public IterableIterator<WrappedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys(); final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() { return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {

View File

@ -209,6 +209,10 @@ public class KeychainIntentService extends IntentService
mMessenger = (Messenger) extras.get(EXTRA_MESSENGER); mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
Bundle data = extras.getBundle(EXTRA_DATA); Bundle data = extras.getBundle(EXTRA_DATA);
if (data == null) {
Log.e(Constants.TAG, "data extra is null!");
return;
}
OtherHelper.logDebugBundle(data, "EXTRA_DATA"); OtherHelper.logDebugBundle(data, "EXTRA_DATA");
@ -320,33 +324,40 @@ public class KeychainIntentService extends IntentService
try { try {
/* Input */ /* Input */
SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
long masterKeyId = saveParcel.mMasterKeyId; if (saveParcel == null) {
Log.e(Constants.TAG, "bug: missing save_keyring_parcel in data!");
return;
}
/* Operation */ /* Operation */
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 50, 100)); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 50, 100));
try { try {
String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
OperationLog log = new OperationLog(); OperationLog log = new OperationLog();
UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, UncachedKeyRing ring;
passphrase, log, 0); if (saveParcel.mMasterKeyId != null) {
providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
WrappedSecretKeyRing secRing =
providerHelper.getWrappedSecretKeyRing(saveParcel.mMasterKeyId);
ring = keyOperations.modifySecretKeyRing(secRing, saveParcel,
passphrase, log, 0);
} else {
ring = keyOperations.createSecretKeyRing(saveParcel, log, 0);
}
providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 10, 95, 100));
if (saveParcel.newPassphrase != null) {
PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
saveParcel.newPassphrase);
}
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
// UncachedKeyRing ring = keyOperations.(saveParcel); //new Keyring
// save the pair
setProgress(R.string.progress_saving_key_ring, 95, 100);
// providerHelper.saveSecretKeyRing(ring);
sendErrorToHandler(e); sendErrorToHandler(e);
} }
setProgress(R.string.progress_done, 100, 100); setProgress(R.string.progress_done, 100, 100);
if (saveParcel.newPassphrase != null) {
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, saveParcel.newPassphrase);
}
/* Output */ /* Output */
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) { } catch (Exception e) {
@ -437,7 +448,7 @@ public class KeychainIntentService extends IntentService
new FileOutputStream(outputFile)); new FileOutputStream(outputFile));
if (mIsCanceled) { if (mIsCanceled) {
boolean isDeleted = new File(outputFile).delete(); new File(outputFile).delete();
} }
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
@ -593,6 +604,7 @@ public class KeychainIntentService extends IntentService
return; return;
} }
Message msg = Message.obtain(); Message msg = Message.obtain();
assert msg != null;
msg.arg1 = arg1; msg.arg1 = arg1;
if (arg2 != null) { if (arg2 != null) {
msg.arg2 = arg2; msg.arg2 = arg2;

View File

@ -233,9 +233,14 @@ public class OperationResultParcel implements Parcelable {
MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey), MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey),
MSG_MG_FOUND_NEW (R.string.msg_mg_found_new), MSG_MG_FOUND_NEW (R.string.msg_mg_found_new),
// secret key create
MSG_CR_ERROR_NO_MASTER (R.string.msg_mr),
// secret key modify // secret key modify
MSG_MF (R.string.msg_mr), MSG_MF (R.string.msg_mr),
MSG_MF_ERROR_ENCODE (R.string.msg_mf_error_encode), MSG_MF_ERROR_ENCODE (R.string.msg_mf_error_encode),
MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),
MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),
MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp), MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),
MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig), MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),
MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase), MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase),

View File

@ -22,10 +22,10 @@ import java.util.ArrayList;
*/ */
public class SaveKeyringParcel implements Parcelable { public class SaveKeyringParcel implements Parcelable {
// the master key id to be edited // the master key id to be edited. if this is null, a new one will be created
public final long mMasterKeyId; public Long mMasterKeyId;
// the key fingerprint, for safety // the key fingerprint, for safety. MUST be null for a new key.
public final byte[] mFingerprint; public byte[] mFingerprint;
public String newPassphrase; public String newPassphrase;
@ -38,9 +38,7 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> revokeUserIds; public ArrayList<String> revokeUserIds;
public ArrayList<Long> revokeSubKeys; public ArrayList<Long> revokeSubKeys;
public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) { public SaveKeyringParcel() {
mMasterKeyId = masterKeyId;
mFingerprint = fingerprint;
addUserIds = new ArrayList<String>(); addUserIds = new ArrayList<String>();
addSubKeys = new ArrayList<SubkeyAdd>(); addSubKeys = new ArrayList<SubkeyAdd>();
changeSubKeys = new ArrayList<SubkeyChange>(); changeSubKeys = new ArrayList<SubkeyChange>();
@ -48,6 +46,12 @@ public class SaveKeyringParcel implements Parcelable {
revokeSubKeys = new ArrayList<Long>(); revokeSubKeys = new ArrayList<Long>();
} }
public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
this();
mMasterKeyId = masterKeyId;
mFingerprint = fingerprint;
}
// performance gain for using Parcelable here would probably be negligible, // performance gain for using Parcelable here would probably be negligible,
// use Serializable instead. // use Serializable instead.
public static class SubkeyAdd implements Serializable { public static class SubkeyAdd implements Serializable {
@ -75,7 +79,7 @@ public class SaveKeyringParcel implements Parcelable {
} }
public SaveKeyringParcel(Parcel source) { public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readLong(); mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray(); mFingerprint = source.createByteArray();
addUserIds = source.createStringArrayList(); addUserIds = source.createStringArrayList();
@ -90,7 +94,10 @@ public class SaveKeyringParcel implements Parcelable {
@Override @Override
public void writeToParcel(Parcel destination, int flags) { public void writeToParcel(Parcel destination, int flags) {
destination.writeLong(mMasterKeyId); destination.writeInt(mMasterKeyId == null ? 0 : 1);
if(mMasterKeyId != null) {
destination.writeLong(mMasterKeyId);
}
destination.writeByteArray(mFingerprint); destination.writeByteArray(mFingerprint);
destination.writeStringList(addUserIds); destination.writeStringList(addUserIds);

View File

@ -626,6 +626,8 @@
<!-- modifySecretKeyRing --> <!-- modifySecretKeyRing -->
<string name="msg_mr">Modifying keyring %s</string> <string name="msg_mr">Modifying keyring %s</string>
<string name="msg_mf_error_encode">Encoding exception!</string> <string name="msg_mf_error_encode">Encoding exception!</string>
<string name="msg_mf_error_fingerprint">Actual key fingerprint does not match expected!</string>
<string name="msg_mf_error_keyid">No keyid. This is a programming error, please file a bug report!</string>
<string name="msg_mf_error_pgp">PGP internal exception!</string> <string name="msg_mf_error_pgp">PGP internal exception!</string>
<string name="msg_mf_error_sig">Signature exception!</string> <string name="msg_mf_error_sig">Signature exception!</string>
<string name="msg_mf_passphrase">Changing passphrase</string> <string name="msg_mf_passphrase">Changing passphrase</string>

View File

@ -22,7 +22,6 @@ public class PgpDecryptVerifyTest {
Assert.assertEquals(expectedSignatureResult, status); Assert.assertEquals(expectedSignatureResult, status);
} }
@Test @Test
public void testVerifyFailure() throws Exception { public void testVerifyFailure() throws Exception {