mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -05:00
Merge pull request #501 from Valodim/master
factor out android dependencies from PgpKeyOperation, and add JUnit/Robolectric testing stubs
This commit is contained in:
commit
247ad6207a
@ -1,5 +1,12 @@
|
||||
apply plugin: 'android'
|
||||
|
||||
sourceSets {
|
||||
testLocal {
|
||||
java.srcDir file('src/test/java')
|
||||
resources.srcDir file('src/test/resources')
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile 'com.android.support:appcompat-v7:19.0.1'
|
||||
@ -15,6 +22,25 @@ dependencies {
|
||||
compile project(':libraries:spongycastle:pkix')
|
||||
compile project(':libraries:spongycastle:prov')
|
||||
compile project(':libraries:Android-AppMsg:library')
|
||||
|
||||
// Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well
|
||||
testLocalCompile 'junit:junit:4.11'
|
||||
testLocalCompile 'org.robolectric:robolectric:2.1.+'
|
||||
testLocalCompile 'com.google.android:android:4.1.1.4'
|
||||
testLocalCompile 'com.android.support:support-v4:19.0.1'
|
||||
testLocalCompile 'com.android.support:appcompat-v7:19.0.1'
|
||||
testLocalCompile project(':OpenPGP-Keychain-API:libraries:openpgp-api-library')
|
||||
testLocalCompile project(':OpenPGP-Keychain-API:libraries:openkeychain-api-library')
|
||||
testLocalCompile project(':libraries:HtmlTextView')
|
||||
testLocalCompile project(':libraries:StickyListHeaders:library')
|
||||
testLocalCompile project(':libraries:AndroidBootstrap')
|
||||
testLocalCompile project(':libraries:zxing')
|
||||
testLocalCompile project(':libraries:zxing-android-integration')
|
||||
testLocalCompile project(':libraries:spongycastle:core')
|
||||
testLocalCompile project(':libraries:spongycastle:pg')
|
||||
testLocalCompile project(':libraries:spongycastle:pkix')
|
||||
testLocalCompile project(':libraries:spongycastle:prov')
|
||||
testLocalCompile project(':libraries:Android-AppMsg:library')
|
||||
}
|
||||
|
||||
android {
|
||||
@ -61,3 +87,19 @@ android {
|
||||
htmlOutput file("lint-report.html")
|
||||
}
|
||||
}
|
||||
|
||||
task localTest(type: Test, dependsOn: assemble) {
|
||||
testClassesDir = sourceSets.testLocal.output.classesDir
|
||||
|
||||
android.sourceSets.main.java.srcDirs.each { dir ->
|
||||
def buildDir = dir.getAbsolutePath().split('/')
|
||||
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
|
||||
|
||||
sourceSets.testLocal.compileClasspath += files(buildDir)
|
||||
sourceSets.testLocal.runtimeClasspath += files(buildDir)
|
||||
}
|
||||
|
||||
classpath = sourceSets.testLocal.runtimeClasspath
|
||||
}
|
||||
|
||||
check.dependsOn localTest
|
||||
|
@ -17,20 +17,6 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.TimeZone;
|
||||
import android.content.Context;
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
@ -46,18 +32,11 @@ import org.spongycastle.openpgp.operator.jcajce.*;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.util.Primes;
|
||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Primes;
|
||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
@ -69,9 +48,17 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/** This class is the single place where ALL operations that actually modify a PGP public or secret
|
||||
* key take place.
|
||||
*
|
||||
* Note that no android specific stuff should be done here, ie no imports from com.android.
|
||||
*
|
||||
* All operations support progress reporting to a ProgressDialogUpdater passed on initialization.
|
||||
* This indicator may be null.
|
||||
*
|
||||
*/
|
||||
public class PgpKeyOperation {
|
||||
private final Context mContext;
|
||||
private final ProgressDialogUpdater mProgress;
|
||||
private ProgressDialogUpdater mProgress;
|
||||
|
||||
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
|
||||
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
|
||||
@ -83,9 +70,8 @@ public class PgpKeyOperation {
|
||||
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
|
||||
CompressionAlgorithmTags.ZIP};
|
||||
|
||||
public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
|
||||
public PgpKeyOperation(ProgressDialogUpdater progress) {
|
||||
super();
|
||||
this.mContext = context;
|
||||
this.mProgress = progress;
|
||||
}
|
||||
|
||||
@ -101,13 +87,29 @@ public class PgpKeyOperation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new secret key.
|
||||
*
|
||||
* @param algorithmChoice
|
||||
* @param keySize
|
||||
* @param passphrase
|
||||
* @param isMasterKey
|
||||
* @return A newly created PGPSecretKey
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws PGPException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws PgpGeneralMsgIdException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
*/
|
||||
|
||||
// TODO: key flags?
|
||||
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
|
||||
boolean isMasterKey)
|
||||
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
|
||||
PgpGeneralException, InvalidAlgorithmParameterException {
|
||||
PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
|
||||
|
||||
if (keySize < 512) {
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
|
||||
}
|
||||
|
||||
if (passphrase == null) {
|
||||
@ -127,8 +129,7 @@ public class PgpKeyOperation {
|
||||
|
||||
case Id.choice.algorithm.elgamal: {
|
||||
if (isMasterKey) {
|
||||
throw new PgpGeneralException(
|
||||
mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
|
||||
}
|
||||
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
BigInteger p = Primes.getBestPrime(keySize);
|
||||
@ -150,8 +151,7 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new PgpGeneralException(
|
||||
mContext.getString(R.string.error_unknown_algorithm_choice));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,8 +171,9 @@ public class PgpKeyOperation {
|
||||
sha1Calc, isMasterKey, keyEncryptor);
|
||||
}
|
||||
|
||||
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
||||
String newPassPhrase) throws IOException, PGPException {
|
||||
public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
||||
String newPassPhrase) throws IOException, PGPException,
|
||||
NoSuchProviderException {
|
||||
|
||||
updateProgress(R.string.progress_building_key, 0, 100);
|
||||
if (oldPassPhrase == null) {
|
||||
@ -190,16 +191,16 @@ public class PgpKeyOperation {
|
||||
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
|
||||
.getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray()));
|
||||
|
||||
updateProgress(R.string.progress_saving_key_ring, 50, 100);
|
||||
|
||||
ProviderHelper.saveKeyRing(mContext, newKeyRing);
|
||||
|
||||
updateProgress(R.string.progress_done, 100, 100);
|
||||
return newKeyRing;
|
||||
|
||||
}
|
||||
|
||||
private void buildNewSecretKey(ArrayList<String> userIds, ArrayList<PGPSecretKey> keys, ArrayList<GregorianCalendar> keysExpiryDates, ArrayList<Integer> keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException,
|
||||
PGPException, SignatureException, IOException {
|
||||
private Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildNewSecretKey(
|
||||
ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
|
||||
ArrayList<GregorianCalendar> keysExpiryDates,
|
||||
ArrayList<Integer> keysUsages,
|
||||
String newPassPhrase, String oldPassPhrase)
|
||||
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
|
||||
|
||||
int usageId = keysUsages.get(0);
|
||||
boolean canSign;
|
||||
@ -210,8 +211,6 @@ public class PgpKeyOperation {
|
||||
// this removes all userIds and certifications previously attached to the masterPublicKey
|
||||
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
|
||||
|
||||
PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray());
|
||||
PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
|
||||
@ -249,7 +248,7 @@ public class PgpKeyOperation {
|
||||
//here we purposefully ignore partial days in each date - long type has no fractional part!
|
||||
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
|
||||
if (numDays <= 0)
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
|
||||
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
|
||||
} else {
|
||||
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
|
||||
@ -319,9 +318,11 @@ public class PgpKeyOperation {
|
||||
GregorianCalendar expiryDate = keysExpiryDates.get(i);
|
||||
//note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
|
||||
//here we purposefully ignore partial days in each date - long type has no fractional part!
|
||||
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
|
||||
if (numDays <= 0)
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
|
||||
long numDays =
|
||||
(expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
|
||||
if (numDays <= 0) {
|
||||
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
|
||||
}
|
||||
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
|
||||
} else {
|
||||
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
|
||||
@ -334,23 +335,18 @@ public class PgpKeyOperation {
|
||||
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
|
||||
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
|
||||
|
||||
updateProgress(R.string.progress_saving_key_ring, 90, 100);
|
||||
return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
|
||||
|
||||
ProviderHelper.saveKeyRing(mContext, secretKeyRing);
|
||||
ProviderHelper.saveKeyRing(mContext, publicKeyRing);
|
||||
|
||||
updateProgress(R.string.progress_done, 100, 100);
|
||||
}
|
||||
|
||||
public void buildSecretKey (SaveKeyringParcel saveParcel)throws PgpGeneralException,
|
||||
PGPException, SignatureException, IOException {
|
||||
public Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildSecretKey (PGPSecretKeyRing mKR,
|
||||
PGPPublicKeyRing pKR,
|
||||
SaveKeyringParcel saveParcel)
|
||||
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
|
||||
|
||||
updateProgress(R.string.progress_building_key, 0, 100);
|
||||
PGPSecretKey masterKey = saveParcel.keys.get(0);
|
||||
|
||||
PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
|
||||
PGPPublicKeyRing pKR = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, masterKey.getKeyID());
|
||||
|
||||
if (saveParcel.oldPassPhrase == null) {
|
||||
saveParcel.oldPassPhrase = "";
|
||||
}
|
||||
@ -359,9 +355,8 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
if (mKR == null) {
|
||||
buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
|
||||
return buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
|
||||
saveParcel.keysUsages, saveParcel.newPassPhrase, saveParcel.oldPassPhrase); //new Keyring
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -430,7 +425,7 @@ public class PgpKeyOperation {
|
||||
//here we purposefully ignore partial days in each date - long type has no fractional part!
|
||||
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
|
||||
if (numDays <= 0)
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
|
||||
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
|
||||
} else {
|
||||
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
|
||||
@ -592,7 +587,7 @@ public class PgpKeyOperation {
|
||||
//here we purposefully ignore partial days in each date - long type has no fractional part!
|
||||
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
|
||||
if (numDays <= 0)
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
|
||||
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
|
||||
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
|
||||
} else {
|
||||
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
|
||||
@ -637,7 +632,6 @@ public class PgpKeyOperation {
|
||||
|
||||
//update the passphrase
|
||||
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
|
||||
updateProgress(R.string.progress_saving_key_ring, 90, 100);
|
||||
|
||||
/* additional handy debug info
|
||||
|
||||
@ -660,62 +654,72 @@ public class PgpKeyOperation {
|
||||
|
||||
*/
|
||||
|
||||
return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(mKR, pKR);
|
||||
|
||||
ProviderHelper.saveKeyRing(mContext, mKR);
|
||||
ProviderHelper.saveKeyRing(mContext, pKR);
|
||||
|
||||
updateProgress(R.string.progress_done, 100, 100);
|
||||
}
|
||||
|
||||
public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
|
||||
throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
/**
|
||||
* Certify the given pubkeyid with the given masterkeyid.
|
||||
*
|
||||
* @param certificationKey Certifying key
|
||||
* @param publicKey public key to certify
|
||||
* @param userIds User IDs to certify, must not be null or empty
|
||||
* @param passphrase Passphrase of the secret key
|
||||
* @return A keyring with added certifications
|
||||
*/
|
||||
public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey, List<String> userIds, String passphrase)
|
||||
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
PGPException, SignatureException {
|
||||
if (passphrase == null) {
|
||||
throw new PgpGeneralException("Unable to obtain passphrase");
|
||||
} else {
|
||||
|
||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||
PGPSignatureGenerator signatureGenerator; {
|
||||
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||
PGPSignatureGenerator signatureGenerator; {
|
||||
|
||||
PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
|
||||
if (certificationKey == null) {
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
||||
}
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
|
||||
if (signaturePrivateKey == null) {
|
||||
throw new PgpGeneralException(
|
||||
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||
}
|
||||
|
||||
// TODO: SHA256 fixed?
|
||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
|
||||
if (certificationKey == null) {
|
||||
throw new PgpGeneralMsgIdException(R.string.error_signature_failed);
|
||||
}
|
||||
|
||||
{ // supply signatureGenerator with a SubpacketVector
|
||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||
signatureGenerator.setHashedSubpackets(packetVector);
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
|
||||
if (signaturePrivateKey == null) {
|
||||
throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
|
||||
}
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
PGPPublicKeyRing pubring = ProviderHelper
|
||||
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
|
||||
PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
|
||||
for(String userId : new IterableIterator<String>(userIds.iterator())) {
|
||||
PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
|
||||
signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
|
||||
}
|
||||
pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
|
||||
// TODO: SHA256 fixed?
|
||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
return pubring;
|
||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
|
||||
}
|
||||
|
||||
{ // supply signatureGenerator with a SubpacketVector
|
||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||
signatureGenerator.setHashedSubpackets(packetVector);
|
||||
}
|
||||
|
||||
// fetch public key ring, add the certification and return it
|
||||
for(String userId : new IterableIterator<String>(userIds.iterator())) {
|
||||
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
||||
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
||||
}
|
||||
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
/** Simple static subclass that stores two values.
|
||||
*
|
||||
* This is only used to return a pair of values in one function above. We specifically don't use
|
||||
* com.android.Pair to keep this class free from android dependencies.
|
||||
*/
|
||||
public static class Pair<K, V> {
|
||||
public final K first;
|
||||
public final V second;
|
||||
public Pair(K first, V second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,7 @@ public class PgpGeneralException extends Exception {
|
||||
public PgpGeneralException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public PgpGeneralException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp.exception;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class PgpGeneralMsgIdException extends Exception {
|
||||
static final long serialVersionUID = 0xf812773343L;
|
||||
|
||||
private final int msgId;
|
||||
|
||||
public PgpGeneralMsgIdException(int msgId) {
|
||||
super("msg[" + msgId + "]");
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
public PgpGeneralException getContextualized(Context context) {
|
||||
return new PgpGeneralException(context.getString(msgId), this);
|
||||
}
|
||||
}
|
@ -35,6 +35,8 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.pgp.*;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.*;
|
||||
@ -473,14 +475,26 @@ public class KeychainIntentService extends IntentService
|
||||
|
||||
long masterKeyId = saveParams.keys.get(0).getKeyID();
|
||||
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||
/* Operation */
|
||||
if (!canSign) {
|
||||
keyOperations.changeSecretKeyPassphrase(
|
||||
ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
|
||||
PGPSecretKeyRing keyRing = ProviderHelper
|
||||
.getPGPSecretKeyRingByKeyId(this, masterKeyId);
|
||||
keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
|
||||
oldPassPhrase, newPassPhrase);
|
||||
setProgress(R.string.progress_saving_key_ring, 50, 100);
|
||||
ProviderHelper.saveKeyRing(this, keyRing);
|
||||
setProgress(R.string.progress_done, 100, 100);
|
||||
} else {
|
||||
keyOperations.buildSecretKey(saveParams);
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
|
||||
PGPSecretKeyRing privkey = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
|
||||
PGPPublicKeyRing pubkey = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, masterKeyId);
|
||||
PgpKeyOperation.Pair<PGPSecretKeyRing,PGPPublicKeyRing> pair =
|
||||
keyOperations.buildSecretKey(privkey, pubkey, saveParams);
|
||||
setProgress(R.string.progress_saving_key_ring, 90, 100);
|
||||
ProviderHelper.saveKeyRing(this, pair.first);
|
||||
ProviderHelper.saveKeyRing(this, pair.second);
|
||||
setProgress(R.string.progress_done, 100, 100);
|
||||
}
|
||||
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
|
||||
|
||||
@ -498,7 +512,7 @@ public class KeychainIntentService extends IntentService
|
||||
boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY);
|
||||
|
||||
/* Operation */
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
|
||||
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
|
||||
passphrase, masterKey);
|
||||
|
||||
@ -529,7 +543,7 @@ public class KeychainIntentService extends IntentService
|
||||
getQuantityString(R.plurals.progress_generating, keysTotal),
|
||||
keysCreated,
|
||||
keysTotal);
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
|
||||
|
||||
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||
4096, passphrase, true);
|
||||
@ -771,14 +785,23 @@ public class KeychainIntentService extends IntentService
|
||||
/* Operation */
|
||||
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
|
||||
masterKeyId);
|
||||
if (signaturePassPhrase == null) {
|
||||
throw new PgpGeneralException("Unable to obtain passphrase");
|
||||
}
|
||||
|
||||
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
|
||||
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
|
||||
PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
|
||||
PGPPublicKeyRing publicRing = ProviderHelper
|
||||
.getPGPPublicKeyRingByKeyId(this, pubKeyId);
|
||||
PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
|
||||
PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(this,
|
||||
masterKeyId);
|
||||
publicKey = keyOperation.certifyKey(certificationKey, publicKey,
|
||||
userIds, signaturePassPhrase);
|
||||
publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
|
||||
|
||||
// store the signed key in our local cache
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
|
||||
int retval = pgpImportExport.storeKeyRingInCache(signedPubKeyRing);
|
||||
int retval = pgpImportExport.storeKeyRingInCache(publicRing);
|
||||
if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
|
||||
throw new PgpGeneralException("Failed to store signed key in local cache");
|
||||
}
|
||||
@ -795,7 +818,11 @@ public class KeychainIntentService extends IntentService
|
||||
if (this.mIsCanceled) {
|
||||
return;
|
||||
}
|
||||
Log.e(Constants.TAG, "KeychainIntentService Exception: ", e);
|
||||
// contextualize the exception, if necessary
|
||||
if(e instanceof PgpGeneralMsgIdException) {
|
||||
e = ((PgpGeneralMsgIdException) e).getContextualized(this);
|
||||
}
|
||||
Log.e(Constants.TAG, "ApgService Exception: ", e);
|
||||
e.printStackTrace();
|
||||
|
||||
Bundle data = new Bundle();
|
||||
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
/** This is a simple class that wraps a ProgressDialogUpdater, scaling the progress
|
||||
* values into a specified range.
|
||||
*/
|
||||
public class ProgressScaler implements ProgressDialogUpdater {
|
||||
|
||||
final ProgressDialogUpdater mWrapped;
|
||||
final int mFrom, mTo, mMax;
|
||||
|
||||
public ProgressScaler(ProgressDialogUpdater wrapped, int from, int to, int max) {
|
||||
this.mWrapped = wrapped;
|
||||
this.mFrom = from;
|
||||
this.mTo = to;
|
||||
this.mMax = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
|
||||
*/
|
||||
public void setProgress(String message, int progress, int max) {
|
||||
mWrapped.setProgress(message, mFrom+ progress*(mTo-mFrom)/max, mMax);
|
||||
}
|
||||
|
||||
public void setProgress(int resourceId, int progress, int max) {
|
||||
mWrapped.setProgress(resourceId, progress, mMax);
|
||||
}
|
||||
|
||||
public void setProgress(int progress, int max) {
|
||||
mWrapped.setProgress(progress, max);
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+android:id/text_layout"
|
||||
android:id="@+id/text_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -0,0 +1,46 @@
|
||||
package org.sufficientlysecure.keychain;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.*;
|
||||
import org.spongycastle.openpgp.*;
|
||||
|
||||
@RunWith(RobolectricGradleTestRunner.class)
|
||||
public class PgpKeyOperationTest {
|
||||
|
||||
PGPSecretKey key;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
/* Input */
|
||||
int algorithm = Id.choice.algorithm.dsa;
|
||||
String passphrase = "swag";
|
||||
int keysize = 2048;
|
||||
boolean masterKey = true;
|
||||
|
||||
/* Operation */
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(null);
|
||||
key = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
|
||||
|
||||
System.err.println("initialized, test key: " + PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void certifyKey() {
|
||||
System.err.println("swag");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.sufficientlysecure.keychain;
|
||||
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.robolectric.AndroidManifest;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.res.Fs;
|
||||
import org.robolectric.res.FsFile;
|
||||
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
|
||||
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
|
||||
public RobolectricGradleTestRunner(Class<?> testClass) throws InitializationError {
|
||||
super(testClass);
|
||||
}
|
||||
|
||||
@Override protected AndroidManifest getAppManifest(Config config) {
|
||||
String myAppPath = KeychainApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
String manifestPath = myAppPath + "../../../src/main/AndroidManifest.xml";
|
||||
return createAppManifest(Fs.fileFromPath(manifestPath));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user