Merge pull request #2111 from open-keychain/auto-value

Auto value
This commit is contained in:
Vincent Breitmoser
2017-05-25 19:42:02 +02:00
committed by GitHub
99 changed files with 2074 additions and 2798 deletions
+6
View File
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'witness' apply plugin: 'witness'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'com.github.kt3k.coveralls' apply plugin: 'com.github.kt3k.coveralls'
apply plugin: 'com.neenbedankt.android-apt'
dependencies { dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
@@ -87,6 +88,11 @@ dependencies {
exclude module: 'recyclerview-v7' exclude module: 'recyclerview-v7'
} }
compile 'org.glassfish:javax.annotation:10.0-b28'
provided "com.google.auto.value:auto-value:1.4.1"
apt "com.google.auto.value:auto-value:1.4.1"
apt "com.ryanharter.auto.value:auto-value-parcel:0.2.5"
compile 'com.ryanharter.auto.value:auto-value-parcel-adapter:0.2.5'
} }
// Output of ./gradlew -q calculateChecksums // Output of ./gradlew -q calculateChecksums
@@ -24,6 +24,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat; import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat; import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import java.io.File; import java.io.File;
import java.net.Proxy; import java.net.Proxy;
@@ -178,12 +179,12 @@ public final class Constants {
/** /**
* Default key configuration: 3072 bit RSA (certify, sign, encrypt) * Default key configuration: 3072 bit RSA (certify, sign, encrypt)
*/ */
public static void addDefaultSubkeys(SaveKeyringParcel saveKeyringParcel) { public static void addDefaultSubkeys(SaveKeyringParcel.Builder builder) {
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(SaveKeyringParcel.Algorithm.RSA, builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
3072, null, KeyFlags.CERTIFY_OTHER, 0L)); 3072, null, KeyFlags.CERTIFY_OTHER, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(SaveKeyringParcel.Algorithm.RSA, builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
3072, null, KeyFlags.SIGN_DATA, 0L)); 3072, null, KeyFlags.SIGN_DATA, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(SaveKeyringParcel.Algorithm.RSA, builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
3072, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); 3072, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
} }
@@ -129,7 +129,7 @@ public class ImportKeysListCloudLoader
mEntryList.clear(); mEntryList.clear();
GetKeyResult pendingResult = new GetKeyResult(null, GetKeyResult pendingResult = new GetKeyResult(null,
RequiredInputParcel.createOrbotRequiredOperation(), RequiredInputParcel.createOrbotRequiredOperation(),
new CryptoInputParcel()); CryptoInputParcel.createCryptoInputParcel());
mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, pendingResult); mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, pendingResult);
return; return;
} }
@@ -71,7 +71,7 @@ import org.sufficientlysecure.keychain.util.Log;
* This class receives a source and/or destination of keys as input and performs * This class receives a source and/or destination of keys as input and performs
* all steps for this backup. * all steps for this backup.
* *
* @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries() * see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
* For the backup operation, the input consists of a set of key ids and * For the backup operation, the input consists of a set of key ids and
* either the name of a file or an output uri to write to. * either the name of a file or an output uri to write to.
*/ */
@@ -104,8 +104,8 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
OutputStream outputStream) { OutputStream outputStream) {
OperationLog log = new OperationLog(); OperationLog log = new OperationLog();
if (backupInput.mMasterKeyIds != null) { if (backupInput.getMasterKeyIds() != null) {
log.add(LogType.MSG_BACKUP, 0, backupInput.mMasterKeyIds.length); log.add(LogType.MSG_BACKUP, 0, backupInput.getMasterKeyIds().length);
} else { } else {
log.add(LogType.MSG_BACKUP_ALL, 0); log.add(LogType.MSG_BACKUP_ALL, 0);
} }
@@ -113,7 +113,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
try { try {
Uri plainUri = null; Uri plainUri = null;
OutputStream plainOut; OutputStream plainOut;
if (backupInput.mIsEncrypted) { if (backupInput.getIsEncrypted()) {
if (cryptoInput == null) { if (cryptoInput == null) {
throw new IllegalStateException("Encrypted backup must supply cryptoInput parameter"); throw new IllegalStateException("Encrypted backup must supply cryptoInput parameter");
} }
@@ -121,23 +121,23 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
plainUri = TemporaryFileProvider.createFile(mContext); plainUri = TemporaryFileProvider.createFile(mContext);
plainOut = mContext.getContentResolver().openOutputStream(plainUri); plainOut = mContext.getContentResolver().openOutputStream(plainUri);
} else { } else {
if (backupInput.mOutputUri == null || outputStream != null) { if (backupInput.getOutputUri() == null || outputStream != null) {
throw new IllegalArgumentException("Unencrypted export to output stream is not supported!"); throw new IllegalArgumentException("Unencrypted export to output stream is not supported!");
} else { } else {
plainOut = mContext.getContentResolver().openOutputStream(backupInput.mOutputUri); plainOut = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri());
} }
} }
CountingOutputStream outStream = new CountingOutputStream(new BufferedOutputStream(plainOut)); CountingOutputStream outStream = new CountingOutputStream(new BufferedOutputStream(plainOut));
boolean backupSuccess = exportKeysToStream( boolean backupSuccess = exportKeysToStream(
log, backupInput.mMasterKeyIds, backupInput.mExportSecret, outStream); log, backupInput.getMasterKeyIds(), backupInput.getExportSecret(), outStream);
if (!backupSuccess) { if (!backupSuccess) {
// if there was an error, it will be in the log so we just have to return // if there was an error, it will be in the log so we just have to return
return new ExportResult(ExportResult.RESULT_ERROR, log); return new ExportResult(ExportResult.RESULT_ERROR, log);
} }
if (!backupInput.mIsEncrypted) { if (!backupInput.getIsEncrypted()) {
// log.add(LogType.MSG_EXPORT_NO_ENCRYPT, 1); // log.add(LogType.MSG_EXPORT_NO_ENCRYPT, 1);
log.add(LogType.MSG_BACKUP_SUCCESS, 1); log.add(LogType.MSG_BACKUP_SUCCESS, 1);
return new ExportResult(ExportResult.RESULT_OK, log); return new ExportResult(ExportResult.RESULT_OK, log);
@@ -170,27 +170,29 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
throws FileNotFoundException { throws FileNotFoundException {
PgpSignEncryptOperation signEncryptOperation = new PgpSignEncryptOperation(mContext, mKeyRepository, mProgressable, mCancelled); PgpSignEncryptOperation signEncryptOperation = new PgpSignEncryptOperation(mContext, mKeyRepository, mProgressable, mCancelled);
PgpSignEncryptData data = new PgpSignEncryptData(); PgpSignEncryptData.Builder builder = PgpSignEncryptData.builder();
data.setSymmetricPassphrase(cryptoInput.getPassphrase()); builder.setSymmetricPassphrase(cryptoInput.getPassphrase());
data.setEnableAsciiArmorOutput(backupInput.mEnableAsciiArmorOutput); builder.setEnableAsciiArmorOutput(backupInput.getEnableAsciiArmorOutput());
data.setAddBackupHeader(true); builder.setAddBackupHeader(true);
PgpSignEncryptInputParcel inputParcel = new PgpSignEncryptInputParcel(data); PgpSignEncryptData pgpSignEncryptData = builder.build();
InputStream inStream = mContext.getContentResolver().openInputStream(plainUri); InputStream inStream = mContext.getContentResolver().openInputStream(plainUri);
String filename; String filename;
if (backupInput.mMasterKeyIds != null && backupInput.mMasterKeyIds.length == 1) { long[] masterKeyIds = backupInput.getMasterKeyIds();
filename = Constants.FILE_BACKUP_PREFIX + KeyFormattingUtils.convertKeyIdToHex(backupInput.mMasterKeyIds[0]); if (masterKeyIds != null && masterKeyIds.length == 1) {
filename = Constants.FILE_BACKUP_PREFIX + KeyFormattingUtils.convertKeyIdToHex(
masterKeyIds[0]);
} else { } else {
filename = Constants.FILE_BACKUP_PREFIX + new SimpleDateFormat("yyyy-MM-dd", Locale filename = Constants.FILE_BACKUP_PREFIX + new SimpleDateFormat("yyyy-MM-dd", Locale
.getDefault()).format(new Date()); .getDefault()).format(new Date());
} }
filename += backupInput.mExportSecret ? Constants.FILE_EXTENSION_BACKUP_SECRET : Constants.FILE_EXTENSION_BACKUP_PUBLIC; filename += backupInput.getExportSecret() ? Constants.FILE_EXTENSION_BACKUP_SECRET : Constants.FILE_EXTENSION_BACKUP_PUBLIC;
InputData inputData = new InputData(inStream, exportedDataSize, filename); InputData inputData = new InputData(inStream, exportedDataSize, filename);
OutputStream outStream; OutputStream outStream;
if (backupInput.mOutputUri == null) { if (backupInput.getOutputUri() == null) {
if (outputStream == null) { if (outputStream == null) {
throw new IllegalArgumentException("If output uri is not set, outputStream must not be null!"); throw new IllegalArgumentException("If output uri is not set, outputStream must not be null!");
} }
@@ -199,10 +201,11 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
if (outputStream != null) { if (outputStream != null) {
throw new IllegalArgumentException("If output uri is set, outputStream must null!"); throw new IllegalArgumentException("If output uri is set, outputStream must null!");
} }
outStream = mContext.getContentResolver().openOutputStream(backupInput.mOutputUri); outStream = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri());
} }
return signEncryptOperation.execute(inputParcel, new CryptoInputParcel(), inputData, outStream); return signEncryptOperation.execute(
pgpSignEncryptData, CryptoInputParcel.createCryptoInputParcel(), inputData, outStream);
} }
boolean exportKeysToStream(OperationLog log, long[] masterKeyIds, boolean exportSecret, OutputStream outStream) { boolean exportKeysToStream(OperationLog log, long[] masterKeyIds, boolean exportSecret, OutputStream outStream) {
@@ -41,7 +41,6 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
@@ -83,12 +82,11 @@ public class BenchmarkOperation extends BaseOperation<BenchmarkInputParcel> {
SignEncryptOperation op = SignEncryptOperation op =
new SignEncryptOperation(mContext, mKeyRepository, new SignEncryptOperation(mContext, mKeyRepository,
new ProgressScaler(mProgressable, i*(50/numRepeats), (i+1)*(50/numRepeats), 100), mCancelled); new ProgressScaler(mProgressable, i*(50/numRepeats), (i+1)*(50/numRepeats), 100), mCancelled);
PgpSignEncryptData data = new PgpSignEncryptData(); PgpSignEncryptData.Builder data = PgpSignEncryptData.builder();
data.setSymmetricPassphrase(passphrase); data.setSymmetricPassphrase(passphrase);
data.setSymmetricEncryptionAlgorithm(OpenKeychainSymmetricKeyAlgorithmTags.AES_128); data.setSymmetricEncryptionAlgorithm(SymmetricKeyAlgorithmTags.AES_128);
SignEncryptParcel input = new SignEncryptParcel(data); SignEncryptParcel input = SignEncryptParcel.createSignEncryptParcel(data.build(), buf);
input.setBytes(buf); encryptResult = op.execute(input, CryptoInputParcel.createCryptoInputParcel());
encryptResult = op.execute(input, new CryptoInputParcel());
log.add(encryptResult, 1); log.add(encryptResult, 1);
log.add(LogType.MSG_BENCH_ENC_TIME, 2, log.add(LogType.MSG_BENCH_ENC_TIME, 2,
String.format("%.2f", encryptResult.getResults().get(0).mOperationTime / 1000.0)); String.format("%.2f", encryptResult.getResults().get(0).mOperationTime / 1000.0));
@@ -105,9 +103,10 @@ public class BenchmarkOperation extends BaseOperation<BenchmarkInputParcel> {
PgpDecryptVerifyOperation op = PgpDecryptVerifyOperation op =
new PgpDecryptVerifyOperation(mContext, mKeyRepository, new PgpDecryptVerifyOperation(mContext, mKeyRepository,
new ProgressScaler(mProgressable, 50 +i*(50/numRepeats), 50 +(i+1)*(50/numRepeats), 100)); new ProgressScaler(mProgressable, 50 +i*(50/numRepeats), 50 +(i+1)*(50/numRepeats), 100));
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(encryptResult.getResultBytes()); PgpDecryptVerifyInputParcel.Builder builder = PgpDecryptVerifyInputParcel.builder()
input.setAllowSymmetricDecryption(true); .setInputBytes(encryptResult.getResultBytes())
decryptResult = op.execute(input, new CryptoInputParcel(passphrase)); .setAllowSymmetricDecryption(true);
decryptResult = op.execute(builder.build(), CryptoInputParcel.createCryptoInputParcel(passphrase));
log.add(decryptResult, 1); log.add(decryptResult, 1);
log.add(LogType.MSG_BENCH_DEC_TIME, 2, String.format("%.2f", decryptResult.mOperationTime / 1000.0)); log.add(LogType.MSG_BENCH_DEC_TIME, 2, String.format("%.2f", decryptResult.mOperationTime / 1000.0));
totalTime += decryptResult.mOperationTime; totalTime += decryptResult.mOperationTime;
@@ -76,7 +76,7 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
// Retrieve and unlock secret key // Retrieve and unlock secret key
CanonicalizedSecretKey certificationKey; CanonicalizedSecretKey certificationKey;
long masterKeyId = parcel.mMasterKeyId; long masterKeyId = parcel.getMasterKeyId();
try { try {
log.add(LogType.MSG_CRT_MASTER_FETCH, 1); log.add(LogType.MSG_CRT_MASTER_FETCH, 1);
@@ -121,7 +121,7 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
// Get actual secret key // Get actual secret key
CanonicalizedSecretKeyRing secretKeyRing = CanonicalizedSecretKeyRing secretKeyRing =
mKeyRepository.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId); mKeyRepository.getCanonicalizedSecretKeyRing(parcel.getMasterKeyId());
certificationKey = secretKeyRing.getSecretKey(); certificationKey = secretKeyRing.getSecretKey();
log.add(LogType.MSG_CRT_UNLOCK, 1); log.add(LogType.MSG_CRT_UNLOCK, 1);
@@ -148,7 +148,7 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
cryptoInput.getSignatureTime(), masterKeyId, masterKeyId); cryptoInput.getSignatureTime(), masterKeyId, masterKeyId);
// Work through all requested certifications // Work through all requested certifications
for (CertifyAction action : parcel.mCertifyActions) { for (CertifyAction action : parcel.getCertifyActions()) {
// Check if we were cancelled // Check if we were cancelled
if (checkCancelled()) { if (checkCancelled()) {
@@ -158,14 +158,14 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
try { try {
if (action.mMasterKeyId == parcel.mMasterKeyId) { if (action.getMasterKeyId() == parcel.getMasterKeyId()) {
log.add(LogType.MSG_CRT_ERROR_SELF, 2); log.add(LogType.MSG_CRT_ERROR_SELF, 2);
certifyError += 1; certifyError += 1;
continue; continue;
} }
CanonicalizedPublicKeyRing publicRing = CanonicalizedPublicKeyRing publicRing =
mKeyRepository.getCanonicalizedPublicKeyRing(action.mMasterKeyId); mKeyRepository.getCanonicalizedPublicKeyRing(action.getMasterKeyId());
PgpCertifyOperation op = new PgpCertifyOperation(); PgpCertifyOperation op = new PgpCertifyOperation();
PgpCertifyResult result = op.certify(certificationKey, publicRing, PgpCertifyResult result = op.certify(certificationKey, publicRing,
@@ -205,7 +205,7 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
// these variables are used inside the following loop, but they need to be created only once // these variables are used inside the following loop, but they need to be created only once
UploadOperation uploadOperation = null; UploadOperation uploadOperation = null;
if (parcel.keyServerUri != null) { if (parcel.getParcelableKeyServer() != null) {
uploadOperation = new UploadOperation(mContext, mKeyRepository, mProgressable, mCancelled); uploadOperation = new UploadOperation(mContext, mKeyRepository, mProgressable, mCancelled);
} }
@@ -226,8 +226,8 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
SaveKeyringResult result = mKeyWritableRepository.savePublicKeyRing(certifiedKey); SaveKeyringResult result = mKeyWritableRepository.savePublicKeyRing(certifiedKey);
if (uploadOperation != null) { if (uploadOperation != null) {
UploadKeyringParcel uploadInput = UploadKeyringParcel uploadInput = UploadKeyringParcel.createWithKeyId(
new UploadKeyringParcel(parcel.keyServerUri, certifiedKey.getMasterKeyId()); parcel.getParcelableKeyServer(), certifiedKey.getMasterKeyId());
UploadResult uploadResult = uploadOperation.execute(uploadInput, cryptoInput); UploadResult uploadResult = uploadOperation.execute(uploadInput, cryptoInput);
log.add(uploadResult, 2); log.add(uploadResult, 2);
@@ -47,7 +47,7 @@ public class ChangeUnlockOperation extends BaseReadWriteOperation<ChangeUnlockPa
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_ED, 0); log.add(OperationResult.LogType.MSG_ED, 0);
if (unlockParcel == null || unlockParcel.mMasterKeyId == null) { if (unlockParcel == null || unlockParcel.getMasterKeyId() == null) {
log.add(OperationResult.LogType.MSG_ED_ERROR_NO_PARCEL, 1); log.add(OperationResult.LogType.MSG_ED_ERROR_NO_PARCEL, 1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} }
@@ -60,10 +60,10 @@ public class ChangeUnlockOperation extends BaseReadWriteOperation<ChangeUnlockPa
try { try {
log.add(OperationResult.LogType.MSG_ED_FETCHING, 1, log.add(OperationResult.LogType.MSG_ED_FETCHING, 1,
KeyFormattingUtils.convertKeyIdToHex(unlockParcel.mMasterKeyId)); KeyFormattingUtils.convertKeyIdToHex(unlockParcel.getMasterKeyId()));
CanonicalizedSecretKeyRing secRing = CanonicalizedSecretKeyRing secRing =
mKeyRepository.getCanonicalizedSecretKeyRing(unlockParcel.mMasterKeyId); mKeyRepository.getCanonicalizedSecretKeyRing(unlockParcel.getMasterKeyId());
modifyResult = keyOperations.modifyKeyRingPassphrase(secRing, cryptoInput, unlockParcel); modifyResult = keyOperations.modifyKeyRingPassphrase(secRing, cryptoInput, unlockParcel);
if (modifyResult.isPending()) { if (modifyResult.isPending()) {
@@ -39,7 +39,7 @@ public class ConsolidateOperation extends BaseReadWriteOperation<ConsolidateInpu
@Override @Override
public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel, public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
CryptoInputParcel cryptoInputParcel) { CryptoInputParcel cryptoInputParcel) {
if (consolidateInputParcel.mConsolidateRecovery) { if (consolidateInputParcel.isStartFromRecovery()) {
return mKeyWritableRepository.consolidateDatabaseStep2(mProgressable); return mKeyWritableRepository.consolidateDatabaseStep2(mProgressable);
} else { } else {
return mKeyWritableRepository.consolidateDatabaseStep1(mProgressable); return mKeyWritableRepository.consolidateDatabaseStep1(mProgressable);
@@ -52,8 +52,8 @@ public class DeleteOperation extends BaseReadWriteOperation<DeleteKeyringParcel>
public OperationResult execute(DeleteKeyringParcel deleteKeyringParcel, public OperationResult execute(DeleteKeyringParcel deleteKeyringParcel,
CryptoInputParcel cryptoInputParcel) { CryptoInputParcel cryptoInputParcel) {
long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds; long[] masterKeyIds = deleteKeyringParcel.getMasterKeyIds();
boolean isSecret = deleteKeyringParcel.mIsSecret; boolean isSecret = deleteKeyringParcel.isDeleteSecret();
return onlyDeleteKey(masterKeyIds, isSecret); return onlyDeleteKey(masterKeyIds, isSecret);
} }
@@ -88,13 +88,13 @@ public class EditKeyOperation extends BaseReadWriteOperation<SaveKeyringParcel>
new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled); new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled);
// If a key id is specified, fetch and edit // If a key id is specified, fetch and edit
if (saveParcel.mMasterKeyId != null) { if (saveParcel.getMasterKeyId() != null) {
try { try {
log.add(LogType.MSG_ED_FETCHING, 1, log.add(LogType.MSG_ED_FETCHING, 1,
KeyFormattingUtils.convertKeyIdToHex(saveParcel.mMasterKeyId)); KeyFormattingUtils.convertKeyIdToHex(saveParcel.getMasterKeyId()));
CanonicalizedSecretKeyRing secRing = CanonicalizedSecretKeyRing secRing =
mKeyRepository.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); mKeyRepository.getCanonicalizedSecretKeyRing(saveParcel.getMasterKeyId());
modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel); modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel);
if (modifyResult.isPending()) { if (modifyResult.isPending()) {
@@ -133,7 +133,7 @@ public class EditKeyOperation extends BaseReadWriteOperation<SaveKeyringParcel>
// It's a success, so this must be non-null now // It's a success, so this must be non-null now
UncachedKeyRing ring = modifyResult.getRing(); UncachedKeyRing ring = modifyResult.getRing();
if (saveParcel.isUpload()) { if (saveParcel.isShouldUpload()) {
byte[] keyringBytes; byte[] keyringBytes;
try { try {
UncachedKeyRing publicKeyRing = ring.extractPublicKeyRing(); UncachedKeyRing publicKeyRing = ring.extractPublicKeyRing();
@@ -144,7 +144,7 @@ public class EditKeyOperation extends BaseReadWriteOperation<SaveKeyringParcel>
} }
UploadKeyringParcel exportKeyringParcel = UploadKeyringParcel exportKeyringParcel =
new UploadKeyringParcel(saveParcel.getUploadKeyserver(), keyringBytes); UploadKeyringParcel.createWithKeyringBytes(saveParcel.getUploadKeyserver(), keyringBytes);
UploadResult uploadResult = new UploadOperation( UploadResult uploadResult = new UploadOperation(
mContext, mKeyRepository, new ProgressScaler(mProgressable, 60, 80, 100), mCancelled) mContext, mKeyRepository, new ProgressScaler(mProgressable, 60, 80, 100), mCancelled)
@@ -154,7 +154,7 @@ public class EditKeyOperation extends BaseReadWriteOperation<SaveKeyringParcel>
if (uploadResult.isPending()) { if (uploadResult.isPending()) {
return new EditKeyResult(log, uploadResult); return new EditKeyResult(log, uploadResult);
} else if (!uploadResult.success() && saveParcel.isUploadAtomic()) { } else if (!uploadResult.success() && saveParcel.isShouldUploadAtomic()) {
// if atomic, update fail implies edit operation should also fail and not save // if atomic, update fail implies edit operation should also fail and not save
return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(), cryptoInput); return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(), cryptoInput);
} }
@@ -469,9 +469,9 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
@NonNull @NonNull
@Override @Override
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList; ArrayList<ParcelableKeyRing> keyList = importInput.getKeyList();
ParcelableHkpKeyserver keyServer = importInput.mKeyserver; ParcelableHkpKeyserver keyServer = importInput.getKeyserver();
boolean skipSave = importInput.mSkipSave; boolean skipSave = importInput.isSkipSave();
ImportKeyResult result; ImportKeyResult result;
if (keyList == null) {// import from file, do serially if (keyList == null) {// import from file, do serially
@@ -495,7 +495,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
result = multiThreadedKeyImport(keyList, keyServer, proxy, skipSave); result = multiThreadedKeyImport(keyList, keyServer, proxy, skipSave);
} }
if (!importInput.mSkipSave) { if (!skipSave) {
ContactSyncAdapterService.requestContactsSync(); ContactSyncAdapterService.requestContactsSync();
} }
return result; return result;
@@ -103,10 +103,12 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
PgpDecryptVerifyOperation op = PgpDecryptVerifyOperation op =
new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable); new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable);
decryptInput.setInputUri(input.getInputUri());
currentInputUri = TemporaryFileProvider.createFile(mContext); currentInputUri = TemporaryFileProvider.createFile(mContext);
decryptInput.setOutputUri(currentInputUri);
decryptInput = decryptInput.toBuilder()
.setInputUri(input.getInputUri())
.setOutputUri(currentInputUri)
.build();
decryptResult = op.execute(decryptInput, cryptoInput); decryptResult = op.execute(decryptInput, cryptoInput);
if (decryptResult.isPending()) { if (decryptResult.isPending()) {
@@ -264,9 +266,10 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
} }
detachedSig.close(); detachedSig.close();
PgpDecryptVerifyInputParcel decryptInput = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel decryptInput = PgpDecryptVerifyInputParcel.builder()
decryptInput.setInputUri(uncheckedSignedDataUri); .setInputUri(uncheckedSignedDataUri)
decryptInput.setDetachedSignature(detachedSig.toByteArray()); .setDetachedSignature(detachedSig.toByteArray())
.build();
PgpDecryptVerifyOperation op = PgpDecryptVerifyOperation op =
new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable); new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable);
@@ -152,9 +152,11 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable); PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(mContext, mKeyRepository, mProgressable);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
.setInputBytes(messageBytes)
.build();
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel()); DecryptVerifyResult decryptVerifyResult = op.execute(input, CryptoInputParcel.createCryptoInputParcel());
if (!decryptVerifyResult.success()) { if (!decryptVerifyResult.success()) {
log.add(decryptVerifyResult, 1); log.add(decryptVerifyResult, 1);
@@ -38,7 +38,6 @@ import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel; import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
/** An operation which promotes a public key ring to a secret one. /** An operation which promotes a public key ring to a secret one.
* *
@@ -67,17 +66,18 @@ public class PromoteKeyOperation extends BaseReadWriteOperation<PromoteKeyringPa
try { try {
log.add(LogType.MSG_PR_FETCHING, 1, log.add(LogType.MSG_PR_FETCHING, 1,
KeyFormattingUtils.convertKeyIdToHex(promoteKeyringParcel.mKeyRingId)); KeyFormattingUtils.convertKeyIdToHex(promoteKeyringParcel.getMasterKeyId()));
CanonicalizedPublicKeyRing pubRing = CanonicalizedPublicKeyRing pubRing =
mKeyRepository.getCanonicalizedPublicKeyRing(promoteKeyringParcel.mKeyRingId); mKeyRepository.getCanonicalizedPublicKeyRing(promoteKeyringParcel.getMasterKeyId());
if (promoteKeyringParcel.mSubKeyIds == null) { long[] subKeyIds = promoteKeyringParcel.getSubKeyIds();
if (subKeyIds == null) {
log.add(LogType.MSG_PR_ALL, 1); log.add(LogType.MSG_PR_ALL, 1);
} else { } else {
// sort for binary search // sort for binary search
for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) { for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
long subKeyId = key.getKeyId(); long subKeyId = key.getKeyId();
if (naiveIndexOf(promoteKeyringParcel.mSubKeyIds, subKeyId) != null) { if (naiveIndexOf(subKeyIds, subKeyId) != null) {
log.add(LogType.MSG_PR_SUBKEY_MATCH, 1, log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId)); KeyFormattingUtils.convertKeyIdToHex(subKeyId));
} else { } else {
@@ -88,8 +88,7 @@ public class PromoteKeyOperation extends BaseReadWriteOperation<PromoteKeyringPa
} }
// create divert-to-card secret key from public key // create divert-to-card secret key from public key
promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.mCardAid, promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.getCardAid(), subKeyIds);
promoteKeyringParcel.mSubKeyIds);
} catch (NotFoundException e) { } catch (NotFoundException e) {
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2); log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
@@ -51,9 +51,9 @@ public class RevokeOperation extends BaseReadWriteOperation<RevokeKeyringParcel>
CryptoInputParcel cryptoInputParcel) { CryptoInputParcel cryptoInputParcel) {
// we don't cache passphrases during revocation // we don't cache passphrases during revocation
cryptoInputParcel.mCachePassphrase = false; cryptoInputParcel = cryptoInputParcel.withNoCachePassphrase();
long masterKeyId = revokeKeyringParcel.mMasterKeyId; long masterKeyId = revokeKeyringParcel.getMasterKeyId();
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_REVOKE, 0, log.add(OperationResult.LogType.MSG_REVOKE, 0,
@@ -71,17 +71,18 @@ public class RevokeOperation extends BaseReadWriteOperation<RevokeKeyringParcel>
return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
} }
SaveKeyringParcel saveKeyringParcel = SaveKeyringParcel.Builder saveKeyringParcel =
new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, keyRing.getFingerprint());
// all revoke operations are made atomic as of now // all revoke operations are made atomic as of now
saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.mUpload, true, saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.isShouldUpload(), true,
revokeKeyringParcel.mKeyserver); revokeKeyringParcel.getKeyserver());
saveKeyringParcel.mRevokeSubKeys.add(masterKeyId); saveKeyringParcel.addRevokeSubkey(masterKeyId);
EditKeyResult revokeAndUploadResult = new EditKeyOperation(mContext, EditKeyResult revokeAndUploadResult = new EditKeyOperation(mContext,
mKeyWritableRepository, mProgressable, mCancelled).execute(saveKeyringParcel, cryptoInputParcel); mKeyWritableRepository, mProgressable, mCancelled).execute(
saveKeyringParcel.build(), cryptoInputParcel);
if (revokeAndUploadResult.isPending()) { if (revokeAndUploadResult.isPending()) {
return revokeAndUploadResult; return revokeAndUploadResult;
@@ -26,7 +26,6 @@ import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
@@ -36,13 +35,11 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -76,20 +73,6 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
SecurityTokenSignOperationsBuilder pendingInputBuilder = null; SecurityTokenSignOperationsBuilder pendingInputBuilder = null;
PgpSignEncryptData data = input.getData();
// if signing subkey has not explicitly been set, get first usable subkey capable of signing
if (data.getSignatureMasterKeyId() != Constants.key.none
&& data.getSignatureSubKeyId() == null) {
try {
long signKeyId = mKeyRepository.getCachedPublicKeyRing(
data.getSignatureMasterKeyId()).getSecretSignId();
data.setSignatureSubKeyId(signKeyId);
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found", e);
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
}
}
do { do {
if (checkCancelled()) { if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, 0); log.add(LogType.MSG_OPERATION_CANCELLED, 0);
@@ -98,13 +81,14 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
PgpSignEncryptOperation op = new PgpSignEncryptOperation(mContext, mKeyRepository, PgpSignEncryptOperation op = new PgpSignEncryptOperation(mContext, mKeyRepository,
new ProgressScaler(mProgressable, 100 * count / total, 100 * ++count / total, 100), mCancelled); new ProgressScaler(mProgressable, 100 * count / total, 100 * ++count / total, 100), mCancelled);
PgpSignEncryptInputParcel inputParcel = new PgpSignEncryptInputParcel(input.getData()); PgpSignEncryptInputParcel inputParcel;
if (inputBytes != null) { if (inputBytes != null) {
inputParcel.setInputBytes(inputBytes); inputParcel = PgpSignEncryptInputParcel.createForBytes(
input.getSignEncryptData(), outputUris.pollFirst(), inputBytes);
} else { } else {
inputParcel.setInputUri(inputUris.removeFirst()); inputParcel = PgpSignEncryptInputParcel.createForInputUri(
input.getSignEncryptData(), outputUris.pollFirst(), inputUris.removeFirst());
} }
inputParcel.setOutputUri(outputUris.pollFirst());
PgpSignEncryptResult result = op.execute(inputParcel, cryptoInput); PgpSignEncryptResult result = op.execute(inputParcel, cryptoInput);
results.add(result); results.add(result);
@@ -118,7 +102,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
} }
if (pendingInputBuilder == null) { if (pendingInputBuilder == null) {
pendingInputBuilder = new SecurityTokenSignOperationsBuilder(requiredInput.mSignatureTime, pendingInputBuilder = new SecurityTokenSignOperationsBuilder(requiredInput.mSignatureTime,
data.getSignatureMasterKeyId(), data.getSignatureSubKeyId()); requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
} }
pendingInputBuilder.addAll(requiredInput); pendingInputBuilder.addAll(requiredInput);
} else if (!result.success()) { } else if (!result.success()) {
@@ -96,7 +96,7 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
ParcelableHkpKeyserver hkpKeyserver; ParcelableHkpKeyserver hkpKeyserver;
{ {
hkpKeyserver = uploadInput.mKeyserver; hkpKeyserver = uploadInput.getKeyserver();
log.add(LogType.MSG_UPLOAD_SERVER, 1, hkpKeyserver.toString()); log.add(LogType.MSG_UPLOAD_SERVER, 1, hkpKeyserver.toString());
} }
@@ -110,22 +110,15 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
@Nullable @Nullable
private CanonicalizedPublicKeyRing getPublicKeyringFromInput(OperationLog log, UploadKeyringParcel uploadInput) { private CanonicalizedPublicKeyRing getPublicKeyringFromInput(OperationLog log, UploadKeyringParcel uploadInput) {
boolean hasMasterKeyId = uploadInput.mMasterKeyId != null;
boolean hasKeyringBytes = uploadInput.mUncachedKeyringBytes != null;
if (hasMasterKeyId == hasKeyringBytes) {
throw new IllegalArgumentException("either keyid xor bytes must be non-null for this method call!");
}
try { try {
Long masterKeyId = uploadInput.getMasterKeyId();
if (hasMasterKeyId) { if (masterKeyId != null) {
log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(uploadInput.mMasterKeyId)); log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(masterKeyId));
return mKeyRepository.getCanonicalizedPublicKeyRing(uploadInput.mMasterKeyId); return mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId);
} }
CanonicalizedKeyRing canonicalizedRing = CanonicalizedKeyRing canonicalizedRing =
UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes) UncachedKeyRing.decodeFromData(uploadInput.getUncachedKeyringBytes())
.canonicalize(new OperationLog(), 0, true); .canonicalize(new OperationLog(), 0, true);
if (!CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) { if (!CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) {
throw new IllegalArgumentException("keyring bytes must contain public key ring!"); throw new IllegalArgumentException("keyring bytes must contain public key ring!");
@@ -1,121 +0,0 @@
/*
* Copyright (C) 2016 Vincent Breitmoser <look@my.amazin.horse>
*
* 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.pgp;
import java.util.Arrays;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.bcpg.S2K;
/** This is an immutable and parcelable class which stores the full s2k parametrization
* of an encrypted secret key, i.e. all fields of the {@link S2K} class (type, hash algo,
* iteration count, iv) plus the encryptionAlgorithm. This class is intended to be used
* as key in a HashMap for session key caching purposes, and overrides the
* {@link #hashCode} and {@link #equals} methods in a suitable way.
*
* Note that although it is a rather unlikely scenario that secret keys of the same key
* are encrypted with different ciphers, the encryption algorithm still determines the
* length of the specific session key and thus needs to be considered for purposes of
* session key caching.
*
* @see org.bouncycastle.bcpg.S2K
*/
public class ComparableS2K implements Parcelable {
private final int encryptionAlgorithm;
private final int s2kType;
private final int s2kHashAlgo;
private final long s2kItCount;
private final byte[] s2kIV;
Integer cachedHashCode;
public ComparableS2K(int encryptionAlgorithm, S2K s2k) {
this.encryptionAlgorithm = encryptionAlgorithm;
this.s2kType = s2k.getType();
this.s2kHashAlgo = s2k.getHashAlgorithm();
this.s2kItCount = s2k.getIterationCount();
this.s2kIV = s2k.getIV();
}
protected ComparableS2K(Parcel in) {
encryptionAlgorithm = in.readInt();
s2kType = in.readInt();
s2kHashAlgo = in.readInt();
s2kItCount = in.readLong();
s2kIV = in.createByteArray();
}
@Override
public int hashCode() {
if (cachedHashCode == null) {
cachedHashCode = encryptionAlgorithm;
cachedHashCode = 31 * cachedHashCode + s2kType;
cachedHashCode = 31 * cachedHashCode + s2kHashAlgo;
cachedHashCode = 31 * cachedHashCode + (int) (s2kItCount ^ (s2kItCount >>> 32));
cachedHashCode = 31 * cachedHashCode + Arrays.hashCode(s2kIV);
}
return cachedHashCode;
}
@Override
public boolean equals(Object o) {
boolean isComparableS2K = o instanceof ComparableS2K;
if (!isComparableS2K) {
return false;
}
ComparableS2K other = (ComparableS2K) o;
return encryptionAlgorithm == other.encryptionAlgorithm
&& s2kType == other.s2kType
&& s2kHashAlgo == other.s2kHashAlgo
&& s2kItCount == other.s2kItCount
&& Arrays.equals(s2kIV, other.s2kIV);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(encryptionAlgorithm);
dest.writeInt(s2kType);
dest.writeInt(s2kHashAlgo);
dest.writeLong(s2kItCount);
dest.writeByteArray(s2kIV);
}
public static final Creator<ComparableS2K> CREATOR = new Creator<ComparableS2K>() {
@Override
public ComparableS2K createFromParcel(Parcel in) {
return new ComparableS2K(in);
}
@Override
public ComparableS2K[] newArray(int size) {
return new ComparableS2K[size];
}
};
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Vincent Breitmoser <look@my.amazin.horse>
*
* 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.pgp;
import android.os.Parcelable;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import org.bouncycastle.bcpg.S2K;
/** This is an immutable and parcelable class which stores the full s2k parametrization
* of an encrypted secret key, i.e. all fields of the {@link S2K} class (type, hash algo,
* iteration count, iv) plus the encryptionAlgorithm. This class is intended to be used
* as key in a HashMap for session key caching purposes, and overrides the
* {@link #hashCode} and {@link #equals} methods in a suitable way.
*
* Note that although it is a rather unlikely scenario that secret keys of the same key
* are encrypted with different ciphers, the encryption algorithm still determines the
* length of the specific session key and thus needs to be considered for purposes of
* session key caching.
*
* @see org.bouncycastle.bcpg.S2K
*/
@AutoValue
public abstract class ParcelableS2K implements Parcelable {
abstract int getEncryptionAlgorithm();
abstract int getS2kType();
abstract int getS2kHashAlgo();
abstract long getS2kItCount();
@SuppressWarnings("mutable")
abstract byte[] getS2kIV();
@Memoized
@Override
public abstract int hashCode();
public static ParcelableS2K fromS2K(int encryptionAlgorithm, S2K s2k) {
return new AutoValue_ParcelableS2K(encryptionAlgorithm,
s2k.getType(), s2k.getHashAlgorithm(), s2k.getIterationCount(), s2k.getIV());
}
}
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@@ -80,12 +81,13 @@ public class PgpCertifyOperation {
publicKey.getKeyID(), publicKey.getKeyID()); publicKey.getKeyID(), publicKey.getKeyID());
try { try {
if (action.mUserIds != null) { ArrayList<String> userIds = action.getUserIds();
log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(), if (userIds != null && !userIds.isEmpty()) {
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, userIds.size(),
KeyFormattingUtils.convertKeyIdToHex(action.getMasterKeyId()));
// fetch public key ring, add the certification and return it // fetch public key ring, add the certification and return it
for (String userId : action.mUserIds) { for (String userId : userIds) {
try { try {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
@@ -96,12 +98,13 @@ public class PgpCertifyOperation {
} }
if (action.mUserAttributes != null) { ArrayList<WrappedUserAttribute> userAttributes = action.getUserAttributes();
log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(), if (userAttributes != null && !userAttributes.isEmpty()) {
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, userAttributes.size(),
KeyFormattingUtils.convertKeyIdToHex(action.getMasterKeyId()));
// fetch public key ring, add the certification and return it // fetch public key ring, add the certification and return it
for (WrappedUserAttribute userAttribute : action.mUserAttributes) { for (WrappedUserAttribute userAttribute : userAttributes) {
PGPUserAttributeSubpacketVector vector = userAttribute.getVector(); PGPUserAttributeSubpacketVector vector = userAttribute.getVector();
try { try {
PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey); PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey);
@@ -19,152 +19,69 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import java.util.HashSet; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
public class PgpDecryptVerifyInputParcel implements Parcelable { import com.google.auto.value.AutoValue;
private Uri mInputUri;
private Uri mOutputUri;
private byte[] mInputBytes;
private boolean mAllowSymmetricDecryption; @AutoValue
private HashSet<Long> mAllowedKeyIds; public abstract class PgpDecryptVerifyInputParcel implements Parcelable {
private boolean mDecryptMetadataOnly; @Nullable
private byte[] mDetachedSignature; @SuppressWarnings("mutable")
private String mRequiredSignerFingerprint; abstract byte[] getInputBytes();
private String mSenderAddress;
public PgpDecryptVerifyInputParcel() { @Nullable
abstract Uri getInputUri();
@Nullable
abstract Uri getOutputUri();
abstract boolean isAllowSymmetricDecryption();
abstract boolean isDecryptMetadataOnly();
@Nullable
abstract List<Long> getAllowedKeyIds();
@Nullable
@SuppressWarnings("mutable")
abstract byte[] getDetachedSignature();
@Nullable
abstract String getSenderAddress();
public abstract Builder toBuilder();
public static Builder builder() {
return new AutoValue_PgpDecryptVerifyInputParcel.Builder()
.setAllowSymmetricDecryption(false)
.setDecryptMetadataOnly(false);
} }
public PgpDecryptVerifyInputParcel(Uri inputUri, Uri outputUri) { @AutoValue.Builder
mInputUri = inputUri; public abstract static class Builder {
mOutputUri = outputUri; public abstract Builder setInputBytes(byte[] inputBytes);
public abstract Builder setInputUri(Uri inputUri);
public abstract Builder setOutputUri(Uri outputUri);
public abstract Builder setAllowSymmetricDecryption(boolean allowSymmetricDecryption);
public abstract Builder setDecryptMetadataOnly(boolean decryptMetadataOnly);
public abstract Builder setDetachedSignature(byte[] detachedSignature);
public abstract Builder setSenderAddress(String senderAddress);
public abstract Builder setAllowedKeyIds(List<Long> allowedKeyIds);
abstract List<Long> getAllowedKeyIds();
abstract PgpDecryptVerifyInputParcel autoBuild();
public PgpDecryptVerifyInputParcel build() {
List<Long> allowedKeyIds = getAllowedKeyIds();
if (allowedKeyIds != null) {
setAllowedKeyIds(Collections.unmodifiableList(allowedKeyIds));
} }
return autoBuild();
public PgpDecryptVerifyInputParcel(byte[] inputBytes) {
mInputBytes = inputBytes;
} }
PgpDecryptVerifyInputParcel(Parcel source) {
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mInputUri = source.readParcelable(getClass().getClassLoader());
mOutputUri = source.readParcelable(getClass().getClassLoader());
mInputBytes = source.createByteArray();
mAllowSymmetricDecryption = source.readInt() != 0;
mAllowedKeyIds = (HashSet<Long>) source.readSerializable();
mDecryptMetadataOnly = source.readInt() != 0;
mDetachedSignature = source.createByteArray();
mRequiredSignerFingerprint = source.readString();
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mInputUri, 0);
dest.writeParcelable(mOutputUri, 0);
dest.writeByteArray(mInputBytes);
dest.writeInt(mAllowSymmetricDecryption ? 1 : 0);
dest.writeSerializable(mAllowedKeyIds);
dest.writeInt(mDecryptMetadataOnly ? 1 : 0);
dest.writeByteArray(mDetachedSignature);
dest.writeString(mRequiredSignerFingerprint);
}
byte[] getInputBytes() {
return mInputBytes;
}
public PgpDecryptVerifyInputParcel setInputUri(Uri uri) {
mInputUri = uri;
return this;
}
Uri getInputUri() {
return mInputUri;
}
public PgpDecryptVerifyInputParcel setOutputUri(Uri uri) {
mOutputUri = uri;
return this;
}
Uri getOutputUri() {
return mOutputUri;
}
boolean isAllowSymmetricDecryption() {
return mAllowSymmetricDecryption;
}
public PgpDecryptVerifyInputParcel setAllowSymmetricDecryption(boolean allowSymmetricDecryption) {
mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
}
HashSet<Long> getAllowedKeyIds() {
return mAllowedKeyIds;
}
public PgpDecryptVerifyInputParcel setAllowedKeyIds(HashSet<Long> allowedKeyIds) {
mAllowedKeyIds = allowedKeyIds;
return this;
}
boolean isDecryptMetadataOnly() {
return mDecryptMetadataOnly;
}
public PgpDecryptVerifyInputParcel setDecryptMetadataOnly(boolean decryptMetadataOnly) {
mDecryptMetadataOnly = decryptMetadataOnly;
return this;
}
byte[] getDetachedSignature() {
return mDetachedSignature;
}
public PgpDecryptVerifyInputParcel setDetachedSignature(byte[] detachedSignature) {
mDetachedSignature = detachedSignature;
return this;
}
public PgpDecryptVerifyInputParcel setSenderAddress(String senderAddress) {
mSenderAddress = senderAddress;
return this;
}
public String getSenderAddress() {
return mSenderAddress;
}
String getRequiredSignerFingerprint() {
return mRequiredSignerFingerprint;
}
public PgpDecryptVerifyInputParcel setRequiredSignerFingerprint(String requiredSignerFingerprint) {
mRequiredSignerFingerprint = requiredSignerFingerprint;
return this;
}
public static final Creator<PgpDecryptVerifyInputParcel> CREATOR = new Creator<PgpDecryptVerifyInputParcel>() {
public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) {
return new PgpDecryptVerifyInputParcel(source);
}
public PgpDecryptVerifyInputParcel[] newArray(final int size) {
return new PgpDecryptVerifyInputParcel[size];
}
};
} }
@@ -69,10 +69,9 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem.DecryptVerifySecurityProblemBuilder; import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem.DecryptVerifySecurityProblemBuilder;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength; import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem; import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc; import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmProblem;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
@@ -321,6 +320,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
decryptionResultBuilder.setEncrypted(true); decryptionResultBuilder.setEncrypted(true);
if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) { if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey); decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
cryptoInput = cryptoInput.withCryptoData(esResult.sessionKey, esResult.decryptedSessionKey);
} }
if (esResult.encryptionKeySecurityProblem != null) { if (esResult.encryptionKeySecurityProblem != null) {
@@ -361,10 +361,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
PGPCompressedData compressedData = (PGPCompressedData) dataChunk; PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
plainFact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream());
JcaSkipMarkerPGPObjectFactory fact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream()); dataChunk = plainFact.nextObject();
dataChunk = fact.nextObject();
plainFact = fact;
} }
PgpSignatureChecker signatureChecker = new PgpSignatureChecker( PgpSignatureChecker signatureChecker = new PgpSignatureChecker(
@@ -378,10 +376,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
dataChunk = plainFact.nextObject(); dataChunk = plainFact.nextObject();
} }
OpenPgpMetadata metadata; if (!(dataChunk instanceof PGPLiteralData)) {
if ( ! (dataChunk instanceof PGPLiteralData)) {
log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent); log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
@@ -422,6 +417,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
new Date(literalData.getModificationTime().getTime()).toString()); new Date(literalData.getModificationTime().getTime()).toString());
OpenPgpMetadata metadata;
// return here if we want to decrypt the metadata only // return here if we want to decrypt the metadata only
if (input.isDecryptMetadataOnly()) { if (input.isDecryptMetadataOnly()) {
@@ -820,7 +817,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
result.encryptedData = encryptedDataAsymmetric; result.encryptedData = encryptedDataAsymmetric;
Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys(); Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys();
cryptoInput.addCryptoData(cachedSessionKeys);
if (cachedSessionKeys.size() >= 1) { if (cachedSessionKeys.size() >= 1) {
Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next(); Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next();
result.sessionKey = entry.getKey().array(); result.sessionKey = entry.getKey().array();
@@ -32,6 +32,7 @@ import java.security.spec.ECGenParameterSpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -79,8 +80,10 @@ import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Builder;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenKeyToCardOperationsBuilder; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenKeyToCardOperationsBuilder;
@@ -166,17 +169,17 @@ public class PgpKeyOperation {
try { try {
// Some safety checks // Some safety checks
if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) { if (add.getAlgorithm() == Algorithm.ECDH || add.getAlgorithm() == Algorithm.ECDSA) {
if (add.mCurve == null) { if (add.getCurve() == null) {
log.add(LogType.MSG_CR_ERROR_NO_CURVE, indent); log.add(LogType.MSG_CR_ERROR_NO_CURVE, indent);
return null; return null;
} }
} else { } else {
if (add.mKeySize == null) { if (add.getKeySize() == null) {
log.add(LogType.MSG_CR_ERROR_NO_KEYSIZE, indent); log.add(LogType.MSG_CR_ERROR_NO_KEYSIZE, indent);
return null; return null;
} }
if (add.mKeySize < 2048) { if (add.getKeySize() < 2048) {
log.add(LogType.MSG_CR_ERROR_KEYSIZE_2048, indent); log.add(LogType.MSG_CR_ERROR_KEYSIZE_2048, indent);
return null; return null;
} }
@@ -185,27 +188,27 @@ public class PgpKeyOperation {
int algorithm; int algorithm;
KeyPairGenerator keyGen; KeyPairGenerator keyGen;
switch (add.mAlgorithm) { switch (add.getAlgorithm()) {
case DSA: { case DSA: {
if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { if ((add.getFlags() & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_DSA, indent); log.add(LogType.MSG_CR_ERROR_FLAGS_DSA, indent);
return null; return null;
} }
progress(R.string.progress_generating_dsa, 30); progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(add.mKeySize, new SecureRandom()); keyGen.initialize(add.getKeySize(), new SecureRandom());
algorithm = PGPPublicKey.DSA; algorithm = PGPPublicKey.DSA;
break; break;
} }
case ELGAMAL: { case ELGAMAL: {
if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { if ((add.getFlags() & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent); log.add(LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent);
return null; return null;
} }
progress(R.string.progress_generating_elgamal, 30); progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(add.mKeySize); BigInteger p = Primes.getBestPrime(add.getKeySize());
BigInteger g = new BigInteger("2"); BigInteger g = new BigInteger("2");
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
@@ -218,19 +221,19 @@ public class PgpKeyOperation {
case RSA: { case RSA: {
progress(R.string.progress_generating_rsa, 30); progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(add.mKeySize, new SecureRandom()); keyGen.initialize(add.getKeySize(), new SecureRandom());
algorithm = PGPPublicKey.RSA_GENERAL; algorithm = PGPPublicKey.RSA_GENERAL;
break; break;
} }
case ECDSA: { case ECDSA: {
if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { if ((add.getFlags() & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent); log.add(LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent);
return null; return null;
} }
progress(R.string.progress_generating_ecdsa, 30); progress(R.string.progress_generating_ecdsa, 30);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.getCurve());
keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(ecParamSpec, new SecureRandom()); keyGen.initialize(ecParamSpec, new SecureRandom());
@@ -240,12 +243,12 @@ public class PgpKeyOperation {
case ECDH: { case ECDH: {
// make sure there are no sign or certify flags set // make sure there are no sign or certify flags set
if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { if ((add.getFlags() & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
log.add(LogType.MSG_CR_ERROR_FLAGS_ECDH, indent); log.add(LogType.MSG_CR_ERROR_FLAGS_ECDH, indent);
return null; return null;
} }
progress(R.string.progress_generating_ecdh, 30); progress(R.string.progress_generating_ecdh, 30);
ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.getCurve());
keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(ecParamSpec, new SecureRandom()); keyGen.initialize(ecParamSpec, new SecureRandom());
@@ -285,23 +288,23 @@ public class PgpKeyOperation {
progress(R.string.progress_building_key, 0); progress(R.string.progress_building_key, 0);
indent += 1; indent += 1;
if (saveParcel.mAddSubKeys.isEmpty()) { if (saveParcel.getAddSubKeys().isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_MASTER, indent); log.add(LogType.MSG_CR_ERROR_NO_MASTER, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (saveParcel.mAddUserIds.isEmpty()) { if (saveParcel.getAddUserIds().isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_USER_ID, indent); log.add(LogType.MSG_CR_ERROR_NO_USER_ID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
SubkeyAdd add = saveParcel.mAddSubKeys.remove(0); SubkeyAdd certificationKey = saveParcel.getAddSubKeys().get(0);
if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) { if ((certificationKey.getFlags() & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_CR_ERROR_NO_CERTIFY, indent); log.add(LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (add.mExpiry == null) { if (certificationKey.getExpiry() == null) {
log.add(LogType.MSG_CR_ERROR_NULL_EXPIRY, indent); log.add(LogType.MSG_CR_ERROR_NULL_EXPIRY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
@@ -309,7 +312,7 @@ public class PgpKeyOperation {
Date creationTime = new Date(); Date creationTime = new Date();
subProgressPush(10, 30); subProgressPush(10, 30);
PGPKeyPair keyPair = createKey(add, creationTime, log, indent); PGPKeyPair keyPair = createKey(certificationKey, creationTime, log, indent);
subProgressPop(); subProgressPop();
// return null if this failed (an error will already have been logged by createKey) // return null if this failed (an error will already have been logged by createKey)
@@ -335,9 +338,14 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing( PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
// Remove certification key from remaining SaveKeyringParcel
Builder builder = SaveKeyringParcel.buildUpon(saveParcel);
builder.getMutableAddSubKeys().remove(certificationKey);
saveParcel = builder.build();
subProgressPush(50, 100); subProgressPush(50, 100);
CryptoInputParcel cryptoInput = new CryptoInputParcel(creationTime, new Passphrase("")); CryptoInputParcel cryptoInput = CryptoInputParcel.createCryptoInputParcel(creationTime, new Passphrase(""));
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log, indent); return internal(sKR, masterSecretKey, certificationKey.getFlags(), certificationKey.getExpiry(), cryptoInput, saveParcel, log, indent);
} catch (PGPException e) { } catch (PGPException e) {
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -392,7 +400,7 @@ public class PgpKeyOperation {
progress(R.string.progress_building_key, 0); progress(R.string.progress_building_key, 0);
// Make sure this is called with a proper SaveKeyringParcel // Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) { if (saveParcel.getMasterKeyId() == null || saveParcel.getMasterKeyId() != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent); log.add(LogType.MSG_MF_ERROR_KEYID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
@@ -402,75 +410,29 @@ public class PgpKeyOperation {
PGPSecretKey masterSecretKey = sKR.getSecretKey(); PGPSecretKey masterSecretKey = sKR.getSecretKey();
// Make sure the fingerprint matches // Make sure the fingerprint matches
if (saveParcel.mFingerprint == null || !Arrays.equals(saveParcel.mFingerprint, if (saveParcel.getFingerprint() == null || !Arrays.equals(saveParcel.getFingerprint(),
masterSecretKey.getPublicKey().getFingerprint())) { masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent); log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (saveParcel.isEmpty()) { if (isParcelEmpty(saveParcel)) {
log.add(LogType.MSG_MF_ERROR_NOOP, indent); log.add(LogType.MSG_MF_ERROR_NOOP, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
// Ensure we don't have multiple keys for the same slot. saveParcel = parseSecurityTokenSerialNumberIntoSubkeyChanges(cryptoInput, saveParcel);
boolean hasSign = false;
boolean hasEncrypt = false;
boolean hasAuth = false;
for(SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
if (change.mMoveKeyToSecurityToken) {
// If this is a moveKeyToSecurityToken operation, see if it was completed: look for a hash
// matching the given subkey ID in cryptoData.
byte[] subKeyId = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(subKeyId);
buf.putLong(change.mKeyId).rewind();
byte[] serialNumber = cryptoInput.getCryptoData().get(buf); if (!checkCapabilitiesAreUnique(wsKR, saveParcel, log, indent)) {
if (serialNumber != null) {
change.mMoveKeyToSecurityToken = false;
change.mSecurityTokenSerialNo = serialNumber;
}
}
if (change.mMoveKeyToSecurityToken) {
// Pending moveKeyToSecurityToken operation. Need to make sure that we don't have multiple
// subkeys pending for the same slot.
CanonicalizedSecretKey wsK = wsKR.getSecretKey(change.mKeyId);
if ((wsK.canSign() || wsK.canCertify())) {
if (hasSign) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasSign = true;
}
} else if ((wsK.canEncrypt())) {
if (hasEncrypt) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasEncrypt = true;
}
} else if ((wsK.canAuthenticate())) {
if (hasAuth) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} else {
hasAuth = true;
}
} else {
log.add(LogType.MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD, indent + 1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
}
}
if (isDummy(masterSecretKey) && ! saveParcel.isRestrictedOnly()) { if (isDummy(masterSecretKey) && ! isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_EK_ERROR_DUMMY, indent); log.add(LogType.MSG_EK_ERROR_DUMMY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (isDummy(masterSecretKey) || saveParcel.isRestrictedOnly()) { if (isDummy(masterSecretKey) || isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_MF_RESTRICTED_MODE, indent); log.add(LogType.MSG_MF_RESTRICTED_MODE, indent);
return internalRestricted(sKR, saveParcel, log, indent + 1); return internalRestricted(sKR, saveParcel, log, indent + 1);
} }
@@ -494,6 +456,70 @@ public class PgpKeyOperation {
} }
private SaveKeyringParcel parseSecurityTokenSerialNumberIntoSubkeyChanges(CryptoInputParcel cryptoInput,
SaveKeyringParcel saveParcel) {
SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildUpon(saveParcel);
for (SubkeyChange change : saveParcel.getChangeSubKeys()) {
if (change.getMoveKeyToSecurityToken()) {
// If this is a moveKeyToSecurityToken operation, see if it was completed: look for a hash
// matching the given subkey ID in cryptoData.
byte[] subKeyId = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(subKeyId);
buf.putLong(change.getSubKeyId()).rewind();
byte[] serialNumber = cryptoInput.getCryptoData().get(buf);
if (serialNumber != null) {
builder.addOrReplaceSubkeyChange(
SubkeyChange.createSecurityTokenSerialNo(change.getSubKeyId(), serialNumber));
}
}
}
saveParcel = builder.build();
return saveParcel;
}
private boolean checkCapabilitiesAreUnique(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
OperationLog log, int indent) {
boolean hasSign = false;
boolean hasEncrypt = false;
boolean hasAuth = false;
for (SubkeyChange change : saveParcel.getChangeSubKeys()) {
if (change.getMoveKeyToSecurityToken()) {
// Pending moveKeyToSecurityToken operation. Need to make sure that we don't have multiple
// subkeys pending for the same slot.
CanonicalizedSecretKey wsK = wsKR.getSecretKey(change.getSubKeyId());
if ((wsK.canSign() || wsK.canCertify())) {
if (hasSign) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasSign = true;
}
} else if ((wsK.canEncrypt())) {
if (hasEncrypt) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasEncrypt = true;
}
} else if ((wsK.canAuthenticate())) {
if (hasAuth) {
log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
return false;
} else {
hasAuth = true;
}
} else {
log.add(LogType.MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD, indent + 1);
return false;
}
}
}
return true;
}
private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
int masterKeyFlags, long masterKeyExpiry, int masterKeyFlags, long masterKeyExpiry,
CryptoInputParcel cryptoInput, CryptoInputParcel cryptoInput,
@@ -547,10 +573,11 @@ public class PgpKeyOperation {
// 2a. Add certificates for new user ids // 2a. Add certificates for new user ids
subProgressPush(15, 23); subProgressPush(15, 23);
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) { String changePrimaryUserId = saveParcel.getChangePrimaryUserId();
for (int i = 0; i < saveParcel.getAddUserIds().size(); i++) {
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size())); progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.getAddUserIds().size()));
String userId = saveParcel.mAddUserIds.get(i); String userId = saveParcel.getAddUserIds().get(i);
log.add(LogType.MSG_MF_UID_ADD, indent, userId); log.add(LogType.MSG_MF_UID_ADD, indent, userId);
if ("".equals(userId)) { if ("".equals(userId)) {
@@ -581,8 +608,8 @@ public class PgpKeyOperation {
} }
// if it's supposed to be primary, we can do that here as well // if it's supposed to be primary, we can do that here as well
boolean isPrimary = saveParcel.mChangePrimaryUserId != null boolean isPrimary = changePrimaryUserId != null
&& userId.equals(saveParcel.mChangePrimaryUserId); && userId.equals(changePrimaryUserId);
// generate and add new certificate // generate and add new certificate
try { try {
PGPSignature cert = generateUserIdSignature( PGPSignature cert = generateUserIdSignature(
@@ -599,10 +626,10 @@ public class PgpKeyOperation {
// 2b. Add certificates for new user ids // 2b. Add certificates for new user ids
subProgressPush(23, 32); subProgressPush(23, 32);
for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) { List<WrappedUserAttribute> addUserAttributes = saveParcel.getAddUserAttribute();
for (int i = 0; i < addUserAttributes.size(); i++) {
progress(R.string.progress_modify_adduat, (i - 1) * (100 / saveParcel.mAddUserAttribute.size())); progress(R.string.progress_modify_adduat, (i - 1) * (100 / addUserAttributes.size()));
WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i); WrappedUserAttribute attribute = addUserAttributes.get(i);
switch (attribute.getType()) { switch (attribute.getType()) {
// the 'none' type must not succeed // the 'none' type must not succeed
@@ -635,10 +662,10 @@ public class PgpKeyOperation {
// 2c. Add revocations for revoked user ids // 2c. Add revocations for revoked user ids
subProgressPush(32, 40); subProgressPush(32, 40);
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) { List<String> revokeUserIds = saveParcel.getRevokeUserIds();
for (int i = 0, j = revokeUserIds.size(); i < j; i++) {
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size())); progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / revokeUserIds.size()));
String userId = saveParcel.mRevokeUserIds.get(i); String userId = revokeUserIds.get(i);
log.add(LogType.MSG_MF_UID_REVOKE, indent, userId); log.add(LogType.MSG_MF_UID_REVOKE, indent, userId);
// Make sure the user id exists (yes these are 10 LoC in Java!) // Make sure the user id exists (yes these are 10 LoC in Java!)
@@ -670,12 +697,12 @@ public class PgpKeyOperation {
subProgressPop(); subProgressPop();
// 3. If primary user id changed, generate new certificates for both old and new // 3. If primary user id changed, generate new certificates for both old and new
if (saveParcel.mChangePrimaryUserId != null) { if (changePrimaryUserId != null) {
progress(R.string.progress_modify_primaryuid, 40); progress(R.string.progress_modify_primaryuid, 40);
// keep track if we actually changed one // keep track if we actually changed one
boolean ok = false; boolean ok = false;
log.add(LogType.MSG_MF_UID_PRIMARY, indent, saveParcel.mChangePrimaryUserId); log.add(LogType.MSG_MF_UID_PRIMARY, indent, changePrimaryUserId);
indent += 1; indent += 1;
// we work on the modifiedPublicKey here, to respect new or newly revoked uids // we work on the modifiedPublicKey here, to respect new or newly revoked uids
@@ -716,7 +743,7 @@ public class PgpKeyOperation {
// we definitely should not update certifications of revoked keys, so just leave it. // we definitely should not update certifications of revoked keys, so just leave it.
if (isRevoked) { if (isRevoked) {
// revoked user ids cannot be primary! // revoked user ids cannot be primary!
if (userId.equals(saveParcel.mChangePrimaryUserId)) { if (userId.equals(changePrimaryUserId)) {
log.add(LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent); log.add(LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
@@ -727,7 +754,7 @@ public class PgpKeyOperation {
if (currentCert.getHashedSubPackets() != null if (currentCert.getHashedSubPackets() != null
&& currentCert.getHashedSubPackets().isPrimaryUserID()) { && currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is // if it's the one we want, just leave it as is
if (userId.equals(saveParcel.mChangePrimaryUserId)) { if (userId.equals(changePrimaryUserId)) {
ok = true; ok = true;
continue; continue;
} }
@@ -753,7 +780,7 @@ public class PgpKeyOperation {
// if we are here, this is not currently a primary user id // if we are here, this is not currently a primary user id
// if it should be // if it should be
if (userId.equals(saveParcel.mChangePrimaryUserId)) { if (userId.equals(changePrimaryUserId)) {
// add shiny new primary user id certificate // add shiny new primary user id certificate
log.add(LogType.MSG_MF_PRIMARY_NEW, indent); log.add(LogType.MSG_MF_PRIMARY_NEW, indent);
modifiedPublicKey = PGPPublicKey.removeCertification( modifiedPublicKey = PGPPublicKey.removeCertification(
@@ -801,67 +828,68 @@ public class PgpKeyOperation {
// 4a. For each subkey change, generate new subkey binding certificate // 4a. For each subkey change, generate new subkey binding certificate
subProgressPush(50, 60); subProgressPush(50, 60);
for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) { List<SubkeyChange> changeSubKeys = saveParcel.getChangeSubKeys();
for (int i = 0, j = changeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mChangeSubKeys.size())); progress(R.string.progress_modify_subkeychange, (i-1) * (100 / changeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i); SaveKeyringParcel.SubkeyChange change = changeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_CHANGE, log.add(LogType.MSG_MF_SUBKEY_CHANGE,
indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); PGPSecretKey sKey = sKR.getSecretKey(change.getSubKeyId());
if (sKey == null) { if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING, log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (change.mDummyStrip) { if (change.getDummyStrip()) {
// IT'S DANGEROUS~ // IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key // no really, it is. this operation irrevocably removes the private key data from the key
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey()); sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
} else if (change.mMoveKeyToSecurityToken) { } else if (change.getMoveKeyToSecurityToken()) {
if (checkSecurityTokenCompatibility(sKey, log, indent + 1)) { if (checkSecurityTokenCompatibility(sKey, log, indent + 1)) {
log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1, log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
nfcKeyToCardOps.addSubkey(change.mKeyId); nfcKeyToCardOps.addSubkey(change.getSubKeyId());
} else { } else {
// Appropriate log message already set by checkSecurityTokenCompatibility // Appropriate log message already set by checkSecurityTokenCompatibility
return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} }
} else if (change.mSecurityTokenSerialNo != null) { } else if (change.getSecurityTokenSerialNo() != null) {
// NOTE: Does this code get executed? Or always handled in internalRestricted? // NOTE: Does this code get executed? Or always handled in internalRestricted?
if (change.mSecurityTokenSerialNo.length != 16) { if (change.getSecurityTokenSerialNo().length != 16) {
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL, log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1, log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId), KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()),
Hex.toHexString(change.mSecurityTokenSerialNo, 8, 6)); Hex.toHexString(change.getSecurityTokenSerialNo(), 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mSecurityTokenSerialNo); sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.getSecurityTokenSerialNo());
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
} }
// This doesn't concern us any further // This doesn't concern us any further
if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) { if (!change.getRecertify() && (change.getExpiry() == null && change.getFlags() == null)) {
continue; continue;
} }
// expiry must not be in the past // expiry must not be in the past
if (change.mExpiry != null && change.mExpiry != 0 && if (change.getExpiry() != null && change.getExpiry() != 0 &&
new Date(change.mExpiry*1000).before(new Date())) { new Date(change.getExpiry() * 1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
// if this is the master key, update uid certificates instead // if this is the master key, update uid certificates instead
if (change.mKeyId == masterPublicKey.getKeyID()) { if (change.getSubKeyId() == masterPublicKey.getKeyID()) {
int flags = change.mFlags == null ? masterKeyFlags : change.mFlags; int flags = change.getFlags() == null ? masterKeyFlags : change.getFlags();
long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry; long expiry = change.getExpiry() == null ? masterKeyExpiry : change.getExpiry();
if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) { if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1); log.add(LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
@@ -886,22 +914,22 @@ public class PgpKeyOperation {
PGPPublicKey pKey = sKey.getPublicKey(); PGPPublicKey pKey = sKey.getPublicKey();
// keep old flags, or replace with new ones // keep old flags, or replace with new ones
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags; int flags = change.getFlags() == null ? readKeyFlags(pKey) : change.getFlags();
long expiry; long expiry;
if (change.mExpiry == null) { if (change.getExpiry() == null) {
long valid = pKey.getValidSeconds(); long valid = pKey.getValidSeconds();
expiry = valid == 0 expiry = valid == 0
? 0 ? 0
: pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds(); : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
} else { } else {
expiry = change.mExpiry; expiry = change.getExpiry();
} }
// drop all old signatures, they will be superseded by the new one // drop all old signatures, they will be superseded by the new one
//noinspection unchecked //noinspection unchecked
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) { for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
// special case: if there is a revocation, don't use expiry from before // special case: if there is a revocation, don't use expiry from before
if ( (change.mExpiry == null || change.mExpiry == 0L) if ( (change.getExpiry() == null || change.getExpiry() == 0L)
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
expiry = 0; expiry = 0;
} }
@@ -917,7 +945,7 @@ public class PgpKeyOperation {
// super special case: subkey is allowed to sign, but isn't available // super special case: subkey is allowed to sign, but isn't available
if (subPrivateKey == null) { if (subPrivateKey == null) {
log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED, log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
} else { } else {
@@ -942,10 +970,10 @@ public class PgpKeyOperation {
// 4b. For each subkey revocation, generate new subkey revocation certificate // 4b. For each subkey revocation, generate new subkey revocation certificate
subProgressPush(60, 65); subProgressPush(60, 65);
for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) { List<Long> revokeSubKeys = saveParcel.getRevokeSubKeys();
for (int i = 0, j = revokeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mRevokeSubKeys.size())); progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / revokeSubKeys.size()));
long revocation = saveParcel.mRevokeSubKeys.get(i); long revocation = revokeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_REVOKE, log.add(LogType.MSG_MF_SUBKEY_REVOKE,
indent, KeyFormattingUtils.convertKeyIdToHex(revocation)); indent, KeyFormattingUtils.convertKeyIdToHex(revocation));
@@ -974,38 +1002,38 @@ public class PgpKeyOperation {
// 5. Generate and add new subkeys // 5. Generate and add new subkeys
subProgressPush(70, 90); subProgressPush(70, 90);
for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) { List<SubkeyAdd> addSubKeys = saveParcel.getAddSubKeys();
for (int i = 0, j = addSubKeys.size(); i < j; i++) {
// Check if we were cancelled - again. This operation is expensive so we do it each loop. // Check if we were cancelled - again. This operation is expensive so we do it each loop.
if (checkCancelled()) { if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent); log.add(LogType.MSG_OPERATION_CANCELLED, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
} }
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size())); progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / addSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i); SaveKeyringParcel.SubkeyAdd add = addSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_NEW, indent, log.add(LogType.MSG_MF_SUBKEY_NEW, indent,
KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) ); KeyFormattingUtils.getAlgorithmInfo(add.getAlgorithm(), add.getKeySize(), add.getCurve()) );
if (isDivertToCard(masterSecretKey)) { if (isDivertToCard(masterSecretKey)) {
log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent +1); log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (add.mExpiry == null) { if (add.getExpiry() == null) {
log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1); log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) { if (add.getExpiry() > 0L && new Date(add.getExpiry() * 1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1); log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
// generate a new secret key (privkey only for now) // generate a new secret key (privkey only for now)
subProgressPush( subProgressPush(
(i-1) * (100 / saveParcel.mAddSubKeys.size()), (i-1) * (100 / addSubKeys.size()),
i * (100 / saveParcel.mAddSubKeys.size()) i * (100 / addSubKeys.size())
); );
PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent); PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent);
subProgressPop(); subProgressPop();
@@ -1022,7 +1050,7 @@ public class PgpKeyOperation {
cryptoInput.getSignatureTime(), cryptoInput.getSignatureTime(),
masterPublicKey, masterPrivateKey, masterPublicKey, masterPrivateKey,
getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey, getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey,
add.mFlags, add.mExpiry); add.getFlags(), add.getExpiry());
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
} catch (NfcInteractionNeeded e) { } catch (NfcInteractionNeeded e) {
nfcSignOps.addHash(e.hashToSign, e.hashAlgo); nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
@@ -1058,13 +1086,13 @@ public class PgpKeyOperation {
} }
// 6. If requested, change passphrase // 6. If requested, change passphrase
if (saveParcel.getChangeUnlockParcel() != null) { if (saveParcel.getNewUnlock() != null) {
progress(R.string.progress_modify_passphrase, 90); progress(R.string.progress_modify_passphrase, 90);
log.add(LogType.MSG_MF_PASSPHRASE, indent); log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1; indent += 1;
sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(), sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
saveParcel.getChangeUnlockParcel().mNewPassphrase, log, indent); saveParcel.getNewUnlock().getNewPassphrase(), log, indent);
if (sKR == null) { if (sKR == null) {
// The error has been logged above, just return a bad state // The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1074,21 +1102,21 @@ public class PgpKeyOperation {
} }
// 7. if requested, change PIN and/or Admin PIN on security token // 7. if requested, change PIN and/or Admin PIN on security token
if (saveParcel.mSecurityTokenPin != null) { if (saveParcel.getSecurityTokenPin() != null) {
progress(R.string.progress_modify_pin, 90); progress(R.string.progress_modify_pin, 90);
log.add(LogType.MSG_MF_PIN, indent); log.add(LogType.MSG_MF_PIN, indent);
indent += 1; indent += 1;
nfcKeyToCardOps.setPin(saveParcel.mSecurityTokenPin); nfcKeyToCardOps.setPin(saveParcel.getSecurityTokenPin());
indent -= 1; indent -= 1;
} }
if (saveParcel.mSecurityTokenAdminPin != null) { if (saveParcel.getSecurityTokenAdminPin() != null) {
progress(R.string.progress_modify_admin_pin, 90); progress(R.string.progress_modify_admin_pin, 90);
log.add(LogType.MSG_MF_ADMIN_PIN, indent); log.add(LogType.MSG_MF_ADMIN_PIN, indent);
indent += 1; indent += 1;
nfcKeyToCardOps.setAdminPin(saveParcel.mSecurityTokenAdminPin); nfcKeyToCardOps.setAdminPin(saveParcel.getSecurityTokenAdminPin());
indent -= 1; indent -= 1;
} }
@@ -1139,7 +1167,7 @@ public class PgpKeyOperation {
progress(R.string.progress_modify, 0); progress(R.string.progress_modify, 0);
// Make sure the saveParcel includes only operations available without passphrase! // Make sure the saveParcel includes only operations available without passphrase!
if (!saveParcel.isRestrictedOnly()) { if (!isParcelRestrictedOnly(saveParcel)) {
log.add(LogType.MSG_MF_ERROR_RESTRICTED, indent); log.add(LogType.MSG_MF_ERROR_RESTRICTED, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
@@ -1153,36 +1181,36 @@ public class PgpKeyOperation {
// The only operation we can do here: // The only operation we can do here:
// 4a. Strip secret keys, or change their protection mode (stripped/divert-to-card) // 4a. Strip secret keys, or change their protection mode (stripped/divert-to-card)
subProgressPush(50, 60); subProgressPush(50, 60);
for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) { List<SubkeyChange> changeSubKeys = saveParcel.getChangeSubKeys();
for (int i = 0, j = changeSubKeys.size(); i < j; i++) {
progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / saveParcel.mChangeSubKeys.size())); progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / changeSubKeys.size()));
SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i); SaveKeyringParcel.SubkeyChange change = changeSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_CHANGE, log.add(LogType.MSG_MF_SUBKEY_CHANGE,
indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); PGPSecretKey sKey = sKR.getSecretKey(change.getSubKeyId());
if (sKey == null) { if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING, log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (change.mDummyStrip || change.mSecurityTokenSerialNo != null) { if (change.getDummyStrip() || change.getSecurityTokenSerialNo() != null) {
// IT'S DANGEROUS~ // IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key // no really, it is. this operation irrevocably removes the private key data from the key
if (change.mDummyStrip) { if (change.getDummyStrip()) {
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey()); sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
} else { } else {
// the serial number must be 16 bytes in length // the serial number must be 16 bytes in length
if (change.mSecurityTokenSerialNo.length != 16) { if (change.getSecurityTokenSerialNo().length != 16) {
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL, log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId)); indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1, log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(change.mKeyId), KeyFormattingUtils.convertKeyIdToHex(change.getSubKeyId()),
Hex.toHexString(change.mSecurityTokenSerialNo, 8, 6)); Hex.toHexString(change.getSecurityTokenSerialNo(), 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mSecurityTokenSerialNo); sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.getSecurityTokenSerialNo());
} }
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
} }
@@ -1204,7 +1232,8 @@ public class PgpKeyOperation {
OperationLog log = new OperationLog(); OperationLog log = new OperationLog();
int indent = 0; int indent = 0;
if (changeUnlockParcel.mMasterKeyId == null || changeUnlockParcel.mMasterKeyId != wsKR.getMasterKeyId()) { Long masterKeyId = changeUnlockParcel.getMasterKeyId();
if (masterKeyId == null || masterKeyId != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent); log.add(LogType.MSG_MF_ERROR_KEYID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
@@ -1219,7 +1248,7 @@ public class PgpKeyOperation {
PGPSecretKey masterSecretKey = sKR.getSecretKey(); PGPSecretKey masterSecretKey = sKR.getSecretKey();
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// Make sure the fingerprint matches // Make sure the fingerprint matches
if (changeUnlockParcel.mFingerprint == null || !Arrays.equals(changeUnlockParcel.mFingerprint, if (changeUnlockParcel.getFingerprint()== null || !Arrays.equals(changeUnlockParcel.getFingerprint(),
masterSecretKey.getPublicKey().getFingerprint())) { masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent); log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1245,7 +1274,7 @@ public class PgpKeyOperation {
try { try {
sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(), sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
changeUnlockParcel.mNewPassphrase, log, indent); changeUnlockParcel.getNewPassphrase(), log, indent);
if (sKR == null) { if (sKR == null) {
// The error has been logged above, just return a bad state // The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1697,4 +1726,31 @@ public class PgpKeyOperation {
return true; return true;
} }
/** Returns true iff this parcel does not contain any operations which require a passphrase. */
private static boolean isParcelRestrictedOnly(SaveKeyringParcel saveKeyringParcel) {
if (saveKeyringParcel.getNewUnlock() != null
|| !saveKeyringParcel.getAddUserIds().isEmpty()
|| !saveKeyringParcel.getAddUserAttribute().isEmpty()
|| !saveKeyringParcel.getAddSubKeys().isEmpty()
|| saveKeyringParcel.getChangePrimaryUserId() != null
|| !saveKeyringParcel.getRevokeUserIds().isEmpty()
|| !saveKeyringParcel.getRevokeSubKeys().isEmpty()) {
return false;
}
for (SubkeyChange change : saveKeyringParcel.getChangeSubKeys()) {
if (change.getRecertify() || change.getFlags() != null || change.getExpiry() != null
|| change.getMoveKeyToSecurityToken()) {
return false;
}
}
return true;
}
private static boolean isParcelEmpty(SaveKeyringParcel saveKeyringParcel) {
return isParcelRestrictedOnly(saveKeyringParcel) && saveKeyringParcel.getChangeSubKeys().isEmpty();
}
} }
@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> * Copyright (C) 2017 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -18,226 +18,91 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.bcpg.CompressionAlgorithmTags; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainHashAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
@AutoValue
public abstract class PgpSignEncryptData implements Parcelable {
@Nullable
public abstract String getCharset();
abstract long getAdditionalEncryptId();
@Nullable
public abstract Long getSignatureSubKeyId();
public abstract long getSignatureMasterKeyId();
@Nullable
public abstract Passphrase getSymmetricPassphrase();
@Nullable
@SuppressWarnings("mutable")
public abstract long[] getEncryptionMasterKeyIds();
@Nullable
public abstract List<Long> getAllowedSigningKeyIds();
@Nullable
public abstract String getVersionHeader();
public class PgpSignEncryptData implements Parcelable { public abstract int getCompressionAlgorithm();
private String mVersionHeader = null; public abstract int getSignatureHashAlgorithm();
private boolean mEnableAsciiArmorOutput = false; public abstract int getSymmetricEncryptionAlgorithm();
private int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
private long[] mEncryptionMasterKeyIds = null;
private Passphrase mSymmetricPassphrase = null;
private int mSymmetricEncryptionAlgorithm = PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT;
private long mSignatureMasterKeyId = Constants.key.none;
private Long mSignatureSubKeyId = null;
private int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
private long mAdditionalEncryptId = Constants.key.none;
private String mCharset;
private boolean mCleartextSignature;
private boolean mDetachedSignature = false;
private boolean mHiddenRecipients = false;
private boolean mAddBackupHeader = false;
public PgpSignEncryptData(){ public abstract boolean isEnableAsciiArmorOutput();
public abstract boolean isCleartextSignature();
public abstract boolean isDetachedSignature();
public abstract boolean isAddBackupHeader();
public abstract boolean isHiddenRecipients();
public static Builder builder() {
return new AutoValue_PgpSignEncryptData.Builder()
.setSignatureMasterKeyId(Constants.key.none)
.setAdditionalEncryptId(Constants.key.none)
.setEnableAsciiArmorOutput(false)
.setCleartextSignature(false)
.setDetachedSignature(false)
.setAddBackupHeader(false)
.setHiddenRecipients(false)
.setCompressionAlgorithm(OpenKeychainCompressionAlgorithmTags.USE_DEFAULT)
.setSignatureHashAlgorithm(OpenKeychainHashAlgorithmTags.USE_DEFAULT)
.setSymmetricEncryptionAlgorithm(OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
} }
private PgpSignEncryptData(Parcel source) { @AutoValue.Builder
ClassLoader loader = getClass().getClassLoader(); public abstract static class Builder {
public abstract PgpSignEncryptData build();
mVersionHeader = source.readString(); public abstract Builder setCharset(String charset);
mEnableAsciiArmorOutput = source.readInt() == 1; public abstract Builder setAdditionalEncryptId(long additionalEncryptId);
mCompressionAlgorithm = source.readInt(); public abstract Builder setSignatureSubKeyId(Long signatureSubKeyId);
mEncryptionMasterKeyIds = source.createLongArray(); public abstract Builder setSignatureMasterKeyId(long signatureMasterKeyId);
mSymmetricPassphrase = source.readParcelable(loader); public abstract Builder setSymmetricPassphrase(Passphrase symmetricPassphrase);
mSymmetricEncryptionAlgorithm = source.readInt(); public abstract Builder setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds);
mSignatureMasterKeyId = source.readLong(); public abstract Builder setVersionHeader(String versionHeader);
mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null;
mSignatureHashAlgorithm = source.readInt();
mAdditionalEncryptId = source.readLong();
mCharset = source.readString();
mCleartextSignature = source.readInt() == 1;
mDetachedSignature = source.readInt() == 1;
mHiddenRecipients = source.readInt() == 1;
mAddBackupHeader = source.readInt() == 1;
}
@Override public abstract Builder setCompressionAlgorithm(int compressionAlgorithm);
public int describeContents() { public abstract Builder setSignatureHashAlgorithm(int signatureHashAlgorithm);
return 0; public abstract Builder setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm);
}
@Override public abstract Builder setAddBackupHeader(boolean isAddBackupHeader);
public void writeToParcel(Parcel dest, int flags) { public abstract Builder setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput);
dest.writeString(mVersionHeader); public abstract Builder setCleartextSignature(boolean isCleartextSignature);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); public abstract Builder setDetachedSignature(boolean isDetachedSignature);
dest.writeInt(mCompressionAlgorithm); public abstract Builder setHiddenRecipients(boolean isHiddenRecipients);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeParcelable(mSymmetricPassphrase, 0);
dest.writeInt(mSymmetricEncryptionAlgorithm);
dest.writeLong(mSignatureMasterKeyId);
if (mSignatureSubKeyId != null) {
dest.writeInt(1);
dest.writeLong(mSignatureSubKeyId);
} else {
dest.writeInt(0);
}
dest.writeInt(mSignatureHashAlgorithm);
dest.writeLong(mAdditionalEncryptId);
dest.writeString(mCharset);
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
dest.writeInt(mAddBackupHeader ? 1 : 0);
}
public String getCharset() { abstract Builder setAllowedSigningKeyIds(List<Long> allowedSigningKeyIds);
return mCharset; public Builder setAllowedSigningKeyIds(Collection<Long> allowedSigningKeyIds) {
} setAllowedSigningKeyIds(Collections.unmodifiableList(new ArrayList<>(allowedSigningKeyIds)));
public void setCharset(String mCharset) {
this.mCharset = mCharset;
}
public long getAdditionalEncryptId() {
return mAdditionalEncryptId;
}
public PgpSignEncryptData setAdditionalEncryptId(long additionalEncryptId) {
mAdditionalEncryptId = additionalEncryptId;
return this; return this;
} }
public int getSignatureHashAlgorithm() {
return mSignatureHashAlgorithm;
} }
public PgpSignEncryptData setSignatureHashAlgorithm(int signatureHashAlgorithm) {
mSignatureHashAlgorithm = signatureHashAlgorithm;
return this;
}
public Long getSignatureSubKeyId() {
return mSignatureSubKeyId;
}
public PgpSignEncryptData setSignatureSubKeyId(long signatureSubKeyId) {
mSignatureSubKeyId = signatureSubKeyId;
return this;
}
public long getSignatureMasterKeyId() {
return mSignatureMasterKeyId;
}
public PgpSignEncryptData setSignatureMasterKeyId(long signatureMasterKeyId) {
mSignatureMasterKeyId = signatureMasterKeyId;
return this;
}
public int getSymmetricEncryptionAlgorithm() {
return mSymmetricEncryptionAlgorithm;
}
public PgpSignEncryptData setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
return this;
}
public Passphrase getSymmetricPassphrase() {
return mSymmetricPassphrase;
}
public PgpSignEncryptData setSymmetricPassphrase(Passphrase symmetricPassphrase) {
mSymmetricPassphrase = symmetricPassphrase;
return this;
}
public long[] getEncryptionMasterKeyIds() {
return mEncryptionMasterKeyIds;
}
public PgpSignEncryptData setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
mEncryptionMasterKeyIds = encryptionMasterKeyIds;
return this;
}
public int getCompressionAlgorithm() {
return mCompressionAlgorithm;
}
public PgpSignEncryptData setCompressionAlgorithm(int compressionAlgorithm) {
mCompressionAlgorithm = compressionAlgorithm;
return this;
}
public boolean isEnableAsciiArmorOutput() {
return mEnableAsciiArmorOutput;
}
public String getVersionHeader() {
return mVersionHeader;
}
public PgpSignEncryptData setVersionHeader(String versionHeader) {
mVersionHeader = versionHeader;
return this;
}
public PgpSignEncryptData setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
return this;
}
public PgpSignEncryptData setCleartextSignature(boolean cleartextSignature) {
this.mCleartextSignature = cleartextSignature;
return this;
}
public boolean isCleartextSignature() {
return mCleartextSignature;
}
public PgpSignEncryptData setDetachedSignature(boolean detachedSignature) {
this.mDetachedSignature = detachedSignature;
return this;
}
public boolean isDetachedSignature() {
return mDetachedSignature;
}
public PgpSignEncryptData setHiddenRecipients(boolean hiddenRecipients) {
this.mHiddenRecipients = hiddenRecipients;
return this;
}
public PgpSignEncryptData setAddBackupHeader(boolean addBackupHeader) {
this.mAddBackupHeader = addBackupHeader;
return this;
}
public boolean isAddBackupHeader() {
return mAddBackupHeader;
}
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
public static final Creator<PgpSignEncryptData> CREATOR = new Creator<PgpSignEncryptData>() {
public PgpSignEncryptData createFromParcel(final Parcel source) {
return new PgpSignEncryptData(source);
}
public PgpSignEncryptData[] newArray(final int size) {
return new PgpSignEncryptData[size];
}
};
} }
@@ -18,104 +18,33 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.util.HashSet; import com.google.auto.value.AutoValue;
public class PgpSignEncryptInputParcel implements Parcelable { @AutoValue
public abstract class PgpSignEncryptInputParcel implements Parcelable {
public abstract PgpSignEncryptData getData();
@Nullable
public abstract Uri getOutputUri();
@Nullable
public abstract Uri getInputUri();
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getInputBytes();
private PgpSignEncryptData data; public static PgpSignEncryptInputParcel createForBytes(
PgpSignEncryptData signEncryptData, Uri outputUri, byte[] inputBytes) {
private Uri mInputUri; return new AutoValue_PgpSignEncryptInputParcel(signEncryptData, outputUri, null, inputBytes);
private Uri mOutputUri;
private byte[] mInputBytes;
private HashSet<Long> mAllowedKeyIds;
public PgpSignEncryptInputParcel(PgpSignEncryptData data) {
this.data = data;
} }
PgpSignEncryptInputParcel(Parcel source) { public static PgpSignEncryptInputParcel createForInputUri(
mInputUri = source.readParcelable(getClass().getClassLoader()); PgpSignEncryptData signEncryptData, Uri outputUri, Uri inputUri) {
mOutputUri = source.readParcelable(getClass().getClassLoader()); return new AutoValue_PgpSignEncryptInputParcel(signEncryptData, outputUri, inputUri, null);
mInputBytes = source.createByteArray();
data = source.readParcelable(getClass().getClassLoader());
mAllowedKeyIds = (HashSet<Long>) source.readSerializable();
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mInputUri, 0);
dest.writeParcelable(mOutputUri, 0);
dest.writeByteArray(mInputBytes);
data.writeToParcel(dest, 0);
dest.writeSerializable(mAllowedKeyIds);
}
public void setInputBytes(byte[] inputBytes) {
this.mInputBytes = inputBytes;
}
byte[] getInputBytes() {
return mInputBytes;
}
public PgpSignEncryptInputParcel setInputUri(Uri uri) {
mInputUri = uri;
return this;
}
Uri getInputUri() {
return mInputUri;
}
public PgpSignEncryptInputParcel setOutputUri(Uri uri) {
mOutputUri = uri;
return this;
}
Uri getOutputUri() {
return mOutputUri;
}
public void setData(PgpSignEncryptData data) {
this.data = data;
}
public PgpSignEncryptData getData() {
return data;
}
HashSet<Long> getAllowedKeyIds() {
return mAllowedKeyIds;
}
public void setAllowedKeyIds(HashSet<Long> allowedKeyIds) {
mAllowedKeyIds = allowedKeyIds;
}
public static final Creator<PgpSignEncryptInputParcel> CREATOR = new Creator<PgpSignEncryptInputParcel>() {
public PgpSignEncryptInputParcel createFromParcel(final Parcel source) {
return new PgpSignEncryptInputParcel(source);
}
public PgpSignEncryptInputParcel[] newArray(final int size) {
return new PgpSignEncryptInputParcel[size];
}
};
} }
@@ -19,6 +19,24 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@@ -43,7 +61,11 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainHashAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -56,23 +78,6 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* This class supports a single, low-level, sign/encrypt operation. * This class supports a single, low-level, sign/encrypt operation.
* <p/> * <p/>
@@ -148,7 +153,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
} }
} }
PgpSignEncryptResult result = executeInternal(input, cryptoInput, inputData, outStream); PgpSignEncryptResult result = executeInternal(input.getData(), cryptoInput, inputData, outStream);
if (outStream instanceof ByteArrayOutputStream) { if (outStream instanceof ByteArrayOutputStream) {
byte[] outputData = ((ByteArrayOutputStream) outStream).toByteArray(); byte[] outputData = ((ByteArrayOutputStream) outStream).toByteArray();
result.setOutputBytes(outputData); result.setOutputBytes(outputData);
@@ -158,41 +163,33 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
} }
@NonNull @NonNull
public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input, CryptoInputParcel cryptoInput, public PgpSignEncryptResult execute(PgpSignEncryptData data, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) { InputData inputData, OutputStream outputStream) {
return executeInternal(input, cryptoInput, inputData, outputStream); return executeInternal(data, cryptoInput, inputData, outputStream);
} }
/** /**
* Signs and/or encrypts data based on parameters of class * Signs and/or encrypts data based on parameters of class
*/ */
private PgpSignEncryptResult executeInternal(PgpSignEncryptInputParcel input, CryptoInputParcel cryptoInput, private PgpSignEncryptResult executeInternal(PgpSignEncryptData data, CryptoInputParcel cryptoInput,
InputData inputData, OutputStream outputStream) { InputData inputData, OutputStream outputStream) {
int indent = 0; int indent = 0;
OperationLog log = new OperationLog(); OperationLog log = new OperationLog();
log.add(LogType.MSG_PSE, indent); log.add(LogType.MSG_PSE, indent);
indent += 1; indent += 1;
PgpSignEncryptData data = input.getData();
boolean enableSignature = data.getSignatureMasterKeyId() != Constants.key.none; boolean enableSignature = data.getSignatureMasterKeyId() != Constants.key.none;
boolean enableEncryption = ((data.getEncryptionMasterKeyIds() != null && data.getEncryptionMasterKeyIds().length > 0) boolean enableEncryption = ((data.getEncryptionMasterKeyIds() != null && data.getEncryptionMasterKeyIds().length > 0)
|| data.getSymmetricPassphrase() != null); || data.getSymmetricPassphrase() != null);
boolean enableCompression = (data.getCompressionAlgorithm() != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature int compressionAlgorithm = data.getCompressionAlgorithm();
+ "\nenableEncryption:" + enableEncryption if (compressionAlgorithm == OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
+ "\nenableCompression:" + enableCompression compressionAlgorithm = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
+ "\nenableAsciiArmorOutput:" + data.isEnableAsciiArmorOutput()
+ "\nisHiddenRecipients:" + data.isHiddenRecipients());
// add additional key id to encryption ids (mostly to do self-encryption)
if (enableEncryption && data.getAdditionalEncryptId() != Constants.key.none) {
data.setEncryptionMasterKeyIds(Arrays.copyOf(data.getEncryptionMasterKeyIds(), data.getEncryptionMasterKeyIds().length + 1));
data.getEncryptionMasterKeyIds()[data.getEncryptionMasterKeyIds().length - 1] = data.getAdditionalEncryptId();
} }
Log.d(Constants.TAG, data.toString());
ArmoredOutputStream armorOut = null; ArmoredOutputStream armorOut = null;
OutputStream out; OutputStream out;
if (data.isEnableAsciiArmorOutput()) { if (data.isEnableAsciiArmorOutput()) {
@@ -221,20 +218,26 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
try { try {
long signingMasterKeyId = data.getSignatureMasterKeyId(); long signingMasterKeyId = data.getSignatureMasterKeyId();
long signingSubKeyId = data.getSignatureSubKeyId(); Long signingSubKeyId = data.getSignatureSubKeyId();
if (signingSubKeyId == null) {
try {
signingSubKeyId = mKeyRepository.getCachedPublicKeyRing(signingMasterKeyId).getSecretSignId();
} catch (PgpKeyNotFoundException e) {
log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
CanonicalizedSecretKeyRing signingKeyRing = CanonicalizedSecretKeyRing signingKeyRing =
mKeyRepository.getCanonicalizedSecretKeyRing(signingMasterKeyId); mKeyRepository.getCanonicalizedSecretKeyRing(signingMasterKeyId);
signingKey = signingKeyRing.getSecretKey(data.getSignatureSubKeyId()); signingKey = signingKeyRing.getSecretKey(signingSubKeyId);
if (input.getAllowedKeyIds() != null) { Collection<Long> allowedSigningKeyIds = data.getAllowedSigningKeyIds();
if (!input.getAllowedKeyIds().contains(signingMasterKeyId)) { if (allowedSigningKeyIds != null && !allowedSigningKeyIds.contains(signingMasterKeyId)) {
// this key is in our db, but NOT allowed! // this key is in our db, but NOT allowed!
log.add(LogType.MSG_PSE_ERROR_KEY_NOT_ALLOWED, indent + 1); log.add(LogType.MSG_PSE_ERROR_KEY_NOT_ALLOWED, indent + 1);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_KEY_DISALLOWED, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_KEY_DISALLOWED, log);
} }
}
// Make sure key is not expired or revoked // Make sure key is not expired or revoked
if (signingKeyRing.isExpired() || signingKeyRing.isRevoked() if (signingKeyRing.isExpired() || signingKeyRing.isRevoked()
@@ -300,12 +303,6 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent); log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
// Use requested hash algo
int requestedAlgorithm = data.getSignatureHashAlgorithm();
if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
}
} }
updateProgress(R.string.progress_preparing_streams, 2, 100); updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -314,12 +311,12 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
if (enableEncryption) { if (enableEncryption) {
// Use requested encryption algo // Use requested encryption algo
int algo = data.getSymmetricEncryptionAlgorithm(); int symmetricEncryptionAlgorithm = data.getSymmetricEncryptionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) { if (symmetricEncryptionAlgorithm == OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM; symmetricEncryptionAlgorithm = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM;
} }
JcePGPDataEncryptorBuilder encryptorBuilder = JcePGPDataEncryptorBuilder encryptorBuilder =
new JcePGPDataEncryptorBuilder(algo) new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
.setWithIntegrityPacket(true); .setWithIntegrityPacket(true);
@@ -336,36 +333,28 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_ASYMMETRIC, indent); log.add(LogType.MSG_PSE_ASYMMETRIC, indent);
// Asymmetric encryption // Asymmetric encryption
for (long id : data.getEncryptionMasterKeyIds()) { for (long encryptMasterKeyId : data.getEncryptionMasterKeyIds()) {
try { boolean success = processEncryptionMasterKeyId(indent, log, data, cPk, encryptMasterKeyId);
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing( if (!success) {
KeyRings.buildUnifiedKeyRingUri(id));
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
for (Long subKeyId : encryptSubKeyIds) {
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
if (encryptSubKeyIds.isEmpty()) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
// Make sure key is not expired or revoked
if (keyRing.isExpired() || keyRing.isRevoked()) {
log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
} catch (KeyWritableRepository.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1, long additionalEncryptId = data.getAdditionalEncryptId();
KeyFormattingUtils.convertKeyIdToHex(id)); if (additionalEncryptId != Constants.key.none) {
boolean success = processEncryptionMasterKeyId(indent, log, data, cPk, additionalEncryptId);
if (!success) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
} }
} }
} }
int signatureHashAlgorithm = data.getSignatureHashAlgorithm();
if (signatureHashAlgorithm == OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
signatureHashAlgorithm = PgpSecurityConstants.DEFAULT_HASH_ALGORITHM;
}
/* Initialize signature generator object for later usage */ /* Initialize signature generator object for later usage */
PGPSignatureGenerator signatureGenerator = null; PGPSignatureGenerator signatureGenerator = null;
if (enableSignature) { if (enableSignature) {
@@ -374,7 +363,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
try { try {
boolean cleartext = data.isCleartextSignature() && data.isEnableAsciiArmorOutput() && !enableEncryption; boolean cleartext = data.isCleartextSignature() && data.isEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getDataSignatureGenerator( signatureGenerator = signingKey.getDataSignatureGenerator(
data.getSignatureHashAlgorithm(), cleartext, signatureHashAlgorithm, cleartext,
cryptoInput.getCryptoData(), cryptoInput.getSignatureTime()); cryptoInput.getCryptoData(), cryptoInput.getSignatureTime());
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
log.add(LogType.MSG_PSE_ERROR_NFC, indent); log.add(LogType.MSG_PSE_ERROR_NFC, indent);
@@ -409,15 +398,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
encryptionOut = cPk.open(out, new byte[1 << 16]); encryptionOut = cPk.open(out, new byte[1 << 16]);
if (enableCompression) { if (compressionAlgorithm != CompressionAlgorithmTags.UNCOMPRESSED) {
log.add(LogType.MSG_PSE_COMPRESSING, indent); log.add(LogType.MSG_PSE_COMPRESSING, indent);
// Use preferred compression algo compressGen = new PGPCompressedDataGenerator(compressionAlgorithm);
int algo = data.getCompressionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
}
compressGen = new PGPCompressedDataGenerator(algo);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut)); bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else { } else {
bcpgOut = new BCPGOutputStream(encryptionOut); bcpgOut = new BCPGOutputStream(encryptionOut);
@@ -466,7 +450,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
log.add(LogType.MSG_PSE_SIGNING_CLEARTEXT, indent); log.add(LogType.MSG_PSE_SIGNING_CLEARTEXT, indent);
// write -----BEGIN PGP SIGNED MESSAGE----- // write -----BEGIN PGP SIGNED MESSAGE-----
armorOut.beginClearText(data.getSignatureHashAlgorithm()); armorOut.beginClearText(signatureHashAlgorithm);
InputStream in = new BufferedInputStream(inputData.getInputStream()); InputStream in = new BufferedInputStream(inputData.getInputStream());
final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
@@ -539,14 +523,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
InputStream in = new BufferedInputStream(inputData.getInputStream()); InputStream in = new BufferedInputStream(inputData.getInputStream());
if (enableCompression) { if (compressionAlgorithm != CompressionAlgorithmTags.UNCOMPRESSED) {
// Use preferred compression algo log.add(LogType.MSG_PSE_COMPRESSING, indent);
int algo = data.getCompressionAlgorithm();
if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
}
compressGen = new PGPCompressedDataGenerator(algo); compressGen = new PGPCompressedDataGenerator(compressionAlgorithm);
bcpgOut = new BCPGOutputStream(compressGen.open(out)); bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else { } else {
bcpgOut = new BCPGOutputStream(out); bcpgOut = new BCPGOutputStream(out);
@@ -597,16 +577,15 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
} }
opTime = System.currentTimeMillis() - startTime; opTime = System.currentTimeMillis() - startTime;
Log.d(Constants.TAG, "sign/encrypt time taken: " + String.format("%.2f", Log.d(Constants.TAG, "sign/encrypt time taken: " + String.format("%.2f", opTime / 1000.0) + "s");
opTime / 1000.0) + "s");
// closing outputs // closing outputs
// NOTE: closing needs to be done in the correct order! // NOTE: closing needs to be done in the correct order!
if (encryptionOut != null) {
if (compressGen != null) { if (compressGen != null) {
compressGen.close(); compressGen.close();
} }
if (encryptionOut != null) {
encryptionOut.close(); encryptionOut.close();
} }
// Note: Closing ArmoredOutputStream does not close the underlying stream // Note: Closing ArmoredOutputStream does not close the underlying stream
@@ -653,7 +632,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
} }
result.setDetachedSignature(detachedByteOut.toByteArray()); result.setDetachedSignature(detachedByteOut.toByteArray());
try { try {
String digestName = PGPUtil.getDigestName(data.getSignatureHashAlgorithm()); String digestName = PGPUtil.getDigestName(signatureHashAlgorithm);
// construct micalg parameter according to https://tools.ietf.org/html/rfc3156#section-5 // construct micalg parameter according to https://tools.ietf.org/html/rfc3156#section-5
result.setMicAlgDigestName("pgp-" + digestName.toLowerCase()); result.setMicAlgDigestName("pgp-" + digestName.toLowerCase());
} catch (PGPException e) { } catch (PGPException e) {
@@ -663,6 +642,36 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
return result; return result;
} }
private boolean processEncryptionMasterKeyId(int indent, OperationLog log, PgpSignEncryptData data,
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
try {
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(encryptMasterKeyId));
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
for (Long subKeyId : encryptSubKeyIds) {
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
if (encryptSubKeyIds.isEmpty()) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(encryptMasterKeyId));
return false;
}
// Make sure key is not expired or revoked
if (keyRing.isExpired() || keyRing.isRevoked()) {
log.add(LogType.MSG_PSE_ERROR_REVOKED_OR_EXPIRED, indent);
return false;
}
} catch (KeyWritableRepository.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(encryptMasterKeyId));
return false;
}
return true;
}
/** /**
* Remove whitespaces on line endings * Remove whitespaces on line endings
*/ */
@@ -19,14 +19,17 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import com.google.auto.value.AutoValue;
/** /**
* This parcel stores the input of one or more PgpSignEncrypt operations. * This parcel stores the input of one or more PgpSignEncrypt operations.
* All operations will use the same general parameters, differing only in * All operations will use the same general parameters, differing only in
@@ -39,83 +42,65 @@ import java.util.List;
* - Once the output uris are empty, there must be exactly one input (uri xor bytes) * - Once the output uris are empty, there must be exactly one input (uri xor bytes)
* left, which will be returned in a byte array as part of the result parcel. * left, which will be returned in a byte array as part of the result parcel.
*/ */
public class SignEncryptParcel implements Parcelable { @AutoValue
public abstract class SignEncryptParcel implements Parcelable {
public abstract PgpSignEncryptData getSignEncryptData();
public abstract List<Uri> getInputUris();
public abstract List<Uri> getOutputUris();
@SuppressWarnings("mutable")
@Nullable
public abstract byte[] getBytes();
private PgpSignEncryptData data;
public ArrayList<Uri> mInputUris = new ArrayList<>();
public ArrayList<Uri> mOutputUris = new ArrayList<>();
public byte[] mBytes;
public SignEncryptParcel(PgpSignEncryptData data) {
this.data = data;
}
public SignEncryptParcel(Parcel src) {
mInputUris = src.createTypedArrayList(Uri.CREATOR);
mOutputUris = src.createTypedArrayList(Uri.CREATOR);
mBytes = src.createByteArray();
data = src.readParcelable(getClass().getClassLoader());
}
public boolean isIncomplete() { public boolean isIncomplete() {
return mInputUris.size() > mOutputUris.size(); List<Uri> inputUris = getInputUris();
List<Uri> outputUris = getOutputUris();
if (inputUris == null || outputUris == null) {
throw new IllegalStateException("Invalid operation for bytes-backed SignEncryptParcel!");
}
return inputUris.size() > outputUris.size();
} }
public byte[] getBytes() {
return mBytes; public static SignEncryptParcel createSignEncryptParcel(PgpSignEncryptData signEncryptData, byte[] bytes) {
// noinspection unchecked, it's ok for the empty list
return new AutoValue_SignEncryptParcel(signEncryptData, Collections.EMPTY_LIST, Collections.EMPTY_LIST, bytes);
} }
public void setBytes(byte[] bytes) { public static Builder builder(SignEncryptParcel signEncryptParcel) {
mBytes = bytes; return new Builder(signEncryptParcel.getSignEncryptData())
.addInputUris(signEncryptParcel.getInputUris())
.addOutputUris(signEncryptParcel.getOutputUris());
} }
public List<Uri> getInputUris() { public static Builder builder(PgpSignEncryptData signEncryptData) {
return Collections.unmodifiableList(mInputUris); return new Builder(signEncryptData);
} }
public void addInputUris(Collection<Uri> inputUris) {
mInputUris.addAll(inputUris); public static class Builder {
private final PgpSignEncryptData signEncryptData;
private ArrayList<Uri> inputUris = new ArrayList<>();
private ArrayList<Uri> outputUris = new ArrayList<>();
private Builder(PgpSignEncryptData signEncryptData) {
this.signEncryptData = signEncryptData;
} }
public List<Uri> getOutputUris() { public SignEncryptParcel build() {
return Collections.unmodifiableList(mOutputUris); return new AutoValue_SignEncryptParcel(signEncryptData,
Collections.unmodifiableList(inputUris),
Collections.unmodifiableList(outputUris),
null);
} }
public void addOutputUris(ArrayList<Uri> outputUris) { public Builder addOutputUris(Collection<Uri> outputUris) {
mOutputUris.addAll(outputUris); this.outputUris.addAll(outputUris);
return this;
} }
public Builder addInputUris(Collection<Uri> inputUris) {
public void setData(PgpSignEncryptData data) { this.inputUris.addAll(inputUris);
this.data = data; return this;
} }
public PgpSignEncryptData getData() {
return data;
} }
@Override
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(mInputUris);
dest.writeTypedList(mOutputUris);
dest.writeByteArray(mBytes);
dest.writeParcelable(data, 0);
}
public static final Creator<SignEncryptParcel> CREATOR = new Creator<SignEncryptParcel>() {
public SignEncryptParcel createFromParcel(final Parcel source) {
return new SignEncryptParcel(source);
}
public SignEncryptParcel[] newArray(final int size) {
return new SignEncryptParcel[size];
}
};
} }
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import com.google.auto.value.AutoValue;
import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.Packet; import org.bouncycastle.bcpg.Packet;
@@ -22,6 +22,7 @@ package org.sufficientlysecure.keychain.remote;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@@ -56,9 +57,8 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem; import org.sufficientlysecure.keychain.pgp.DecryptVerifySecurityProblem;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SecurityProblem; import org.sufficientlysecure.keychain.pgp.SecurityProblem;
@@ -109,12 +109,11 @@ public class OpenPgpService extends Service {
boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
// sign-only // sign-only
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEnableAsciiArmorOutput(asciiArmor) pgpData.setEnableAsciiArmorOutput(asciiArmor)
.setCleartextSignature(cleartextSign) .setCleartextSignature(cleartextSign)
.setDetachedSignature(!cleartextSign) .setDetachedSignature(!cleartextSign)
.setVersionHeader(null) .setVersionHeader(null);
.setSignatureHashAlgorithm(PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
Intent signKeyIdIntent = getSignKeyMasterId(data); Intent signKeyIdIntent = getSignKeyMasterId(data);
@@ -132,17 +131,13 @@ public class OpenPgpService extends Service {
// get first usable subkey capable of signing // get first usable subkey capable of signing
try { try {
long signSubKeyId = mKeyRepository.getCachedPublicKeyRing( long signSubKeyId = mKeyRepository.getCachedPublicKeyRing(signKeyId).getSecretSignId();
pgpData.getSignatureMasterKeyId()).getSecretSignId();
pgpData.setSignatureSubKeyId(signSubKeyId); pgpData.setSignatureSubKeyId(signSubKeyId);
} catch (PgpKeyNotFoundException e) { } catch (PgpKeyNotFoundException e) {
throw new Exception("signing subkey not found!", e); throw new Exception("signing subkey not found!", e);
} }
} }
pgpData.setAllowedSigningKeyIds(getAllowedKeyIds());
PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel(pgpData);
pseInput.setAllowedKeyIds(getAllowedKeyIds());
// Get Input- and OutputStream from ParcelFileDescriptor // Get Input- and OutputStream from ParcelFileDescriptor
if (!cleartextSign) { if (!cleartextSign) {
@@ -155,17 +150,17 @@ public class OpenPgpService extends Service {
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) { if (inputParcel == null) {
inputParcel = new CryptoInputParcel(new Date()); inputParcel = CryptoInputParcel.createCryptoInputParcel(new Date());
} }
// override passphrase in input parcel if given by API call // override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
inputParcel.mPassphrase = inputParcel = inputParcel.withPassphrase(
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
// execute PGP operation! // execute PGP operation!
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, mKeyRepository, null); PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, mKeyRepository, null);
PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputParcel, inputData, outputStream); PgpSignEncryptResult pgpResult = pse.execute(pgpData.build(), inputParcel, inputData, outputStream);
if (pgpResult.isPending()) { if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel(); RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
@@ -205,18 +200,14 @@ public class OpenPgpService extends Service {
originalFilename = ""; originalFilename = "";
} }
boolean enableCompression = data.getBooleanExtra(OpenPgpApi.EXTRA_ENABLE_COMPRESSION, true); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder()
int compressionId; .setEnableAsciiArmorOutput(asciiArmor)
if (enableCompression) { .setVersionHeader(null);
compressionId = PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT;
} else {
compressionId = PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED;
}
PgpSignEncryptData pgpData = new PgpSignEncryptData(); boolean enableCompression = data.getBooleanExtra(OpenPgpApi.EXTRA_ENABLE_COMPRESSION, true);
pgpData.setEnableAsciiArmorOutput(asciiArmor) if (!enableCompression) {
.setVersionHeader(null) pgpData.setCompressionAlgorithm(OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
.setCompressionAlgorithm(compressionId); }
if (sign) { if (sign) {
Intent signKeyIdIntent = getSignKeyMasterId(data); Intent signKeyIdIntent = getSignKeyMasterId(data);
@@ -260,17 +251,16 @@ public class OpenPgpService extends Service {
return result; return result;
} }
pgpData.setEncryptionMasterKeyIds(keyIdResult.getKeyIds()); pgpData.setEncryptionMasterKeyIds(keyIdResult.getKeyIds());
pgpData.setAllowedSigningKeyIds(getAllowedKeyIds());
PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel(pgpData);
pseInput.setAllowedKeyIds(getAllowedKeyIds());
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) { if (inputParcel == null) {
inputParcel = new CryptoInputParcel(new Date()); inputParcel = CryptoInputParcel.createCryptoInputParcel(new Date());
} }
// override passphrase in input parcel if given by API call // override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
inputParcel.mPassphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); inputParcel = inputParcel.withPassphrase(
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
// TODO this is not correct! // TODO this is not correct!
@@ -279,7 +269,7 @@ public class OpenPgpService extends Service {
// execute PGP operation! // execute PGP operation!
PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, mKeyRepository, null); PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, mKeyRepository, null);
PgpSignEncryptResult pgpResult = op.execute(pseInput, inputParcel, inputData, outputStream); PgpSignEncryptResult pgpResult = op.execute(pgpData.build(), inputParcel, inputData, outputStream);
if (pgpResult.isPending()) { if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel(); RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
@@ -353,17 +343,18 @@ public class OpenPgpService extends Service {
CryptoInputParcel cryptoInput = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); CryptoInputParcel cryptoInput = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (cryptoInput == null) { if (cryptoInput == null) {
cryptoInput = new CryptoInputParcel(); cryptoInput = CryptoInputParcel.createCryptoInputParcel();
} }
// override passphrase in input parcel if given by API call // override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
cryptoInput.mPassphrase = cryptoInput = cryptoInput.withPassphrase(
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
if (data.hasExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT)) { if (data.hasExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT)) {
OpenPgpDecryptionResult decryptionResult = data.getParcelableExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT); OpenPgpDecryptionResult decryptionResult = data.getParcelableExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT);
if (decryptionResult != null && decryptionResult.hasDecryptedSessionKey()) { if (decryptionResult != null && decryptionResult.hasDecryptedSessionKey()) {
cryptoInput.addCryptoData(decryptionResult.getSessionKey(), decryptionResult.getDecryptedSessionKey()); cryptoInput = cryptoInput.withCryptoData(
decryptionResult.getSessionKey(), decryptionResult.getDecryptedSessionKey());
} }
} }
@@ -377,12 +368,13 @@ public class OpenPgpService extends Service {
// allow only private keys associated with accounts of this app // allow only private keys associated with accounts of this app
// no support for symmetric encryption // no support for symmetric encryption
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel() PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
.setAllowSymmetricDecryption(false) .setAllowSymmetricDecryption(false)
.setAllowedKeyIds(getAllowedKeyIds()) .setAllowedKeyIds(new ArrayList<>(getAllowedKeyIds()))
.setDecryptMetadataOnly(decryptMetadataOnly) .setDecryptMetadataOnly(decryptMetadataOnly)
.setDetachedSignature(detachedSignature) .setDetachedSignature(detachedSignature)
.setSenderAddress(senderAddress); .setSenderAddress(senderAddress)
.build();
DecryptVerifyResult pgpResult = op.execute(input, cryptoInput, inputData, outputStream); DecryptVerifyResult pgpResult = op.execute(input, cryptoInput, inputData, outputStream);
@@ -657,7 +649,8 @@ public class OpenPgpService extends Service {
// after user interaction with RemoteBackupActivity, // after user interaction with RemoteBackupActivity,
// the backup code is cached in CryptoInputParcelCacheService, now we can proceed // the backup code is cached in CryptoInputParcelCacheService, now we can proceed
BackupKeyringParcel input = new BackupKeyringParcel(masterKeyIds, backupSecret, true, enableAsciiArmorOutput, null); BackupKeyringParcel input = BackupKeyringParcel
.createBackupKeyringParcel(masterKeyIds, backupSecret, true, enableAsciiArmorOutput, null);
BackupOperation op = new BackupOperation(this, mKeyRepository, null); BackupOperation op = new BackupOperation(this, mKeyRepository, null);
ExportResult pgpResult = op.execute(input, inputParcel, outputStream); ExportResult pgpResult = op.execute(input, inputParcel, outputStream);
@@ -24,6 +24,8 @@ import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.bcpg.sig.KeyFlags; import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECCurve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
// 4.3.3.6 Algorithm Attributes // 4.3.3.6 Algorithm Attributes
public class ECKeyFormat extends KeyFormat { public class ECKeyFormat extends KeyFormat {
@@ -84,7 +86,7 @@ public class ECKeyFormat extends KeyFormat {
} }
} }
public void addToSaveKeyringParcel(SaveKeyringParcel keyring, int keyFlags) { public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
final X9ECParameters params = NISTNamedCurves.getByOID(mECCurveOID); final X9ECParameters params = NISTNamedCurves.getByOID(mECCurveOID);
final ECCurve curve = params.getCurve(); final ECCurve curve = params.getCurve();
@@ -105,7 +107,6 @@ public class ECKeyFormat extends KeyFormat {
throw new IllegalArgumentException("Unsupported curve " + mECCurveOID); throw new IllegalArgumentException("Unsupported curve " + mECCurveOID);
} }
keyring.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(algo, builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(algo, curve.getFieldSize(), scurve, keyFlags, 0L));
curve.getFieldSize(), scurve, keyFlags, 0L));
} }
} }
@@ -94,6 +94,6 @@ public abstract class KeyFormat {
throw new IllegalArgumentException("Unsupported Algorithm id " + t); throw new IllegalArgumentException("Unsupported Algorithm id " + t);
} }
public abstract void addToSaveKeyringParcel(SaveKeyringParcel keyring, int keyFlags); public abstract void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags);
} }
@@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.securitytoken; package org.sufficientlysecure.keychain.securitytoken;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
// 4.3.3.6 Algorithm Attributes // 4.3.3.6 Algorithm Attributes
public class RSAKeyFormat extends KeyFormat { public class RSAKeyFormat extends KeyFormat {
@@ -84,8 +86,8 @@ public class RSAKeyFormat extends KeyFormat {
} }
} }
public void addToSaveKeyringParcel(SaveKeyringParcel keyring, int keyFlags) { public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
keyring.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(SaveKeyringParcel.Algorithm.RSA, builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
mModulusLength, null, keyFlags, 0L)); mModulusLength, null, keyFlags, 0L));
} }
} }
@@ -19,61 +19,28 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
public class BackupKeyringParcel implements Parcelable { @AutoValue
public Uri mCanonicalizedPublicKeyringUri; public abstract class BackupKeyringParcel implements Parcelable {
@Nullable
@SuppressWarnings("mutable")
public abstract long[] getMasterKeyIds();
public abstract boolean getExportSecret();
public abstract boolean getIsEncrypted();
public abstract boolean getEnableAsciiArmorOutput();
@Nullable
public abstract Uri getOutputUri();
public final boolean mExportSecret; public static BackupKeyringParcel createBackupKeyringParcel(long[] masterKeyIds, boolean exportSecret,
public final boolean mIsEncrypted; boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri) {
public final boolean mEnableAsciiArmorOutput; return new AutoValue_BackupKeyringParcel(
public final long mMasterKeyIds[]; masterKeyIds, exportSecret, isEncrypted, enableAsciiArmorOutput, outputUri);
public final Uri mOutputUri;
public BackupKeyringParcel(long[] masterKeyIds, boolean exportSecret, boolean isEncrypted, boolean enableAsciiArmorOutput, Uri outputUri) {
mMasterKeyIds = masterKeyIds;
mExportSecret = exportSecret;
mOutputUri = outputUri;
mIsEncrypted = isEncrypted;
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
} }
protected BackupKeyringParcel(Parcel in) {
mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
mExportSecret = in.readByte() != 0x00;
mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
mMasterKeyIds = in.createLongArray();
mIsEncrypted = in.readInt() != 0;
mEnableAsciiArmorOutput = in.readInt() != 0;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(mCanonicalizedPublicKeyringUri);
dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
dest.writeValue(mOutputUri);
dest.writeLongArray(mMasterKeyIds);
dest.writeInt(mIsEncrypted ? 1 : 0);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
}
public static final Parcelable.Creator<BackupKeyringParcel> CREATOR = new Parcelable.Creator<BackupKeyringParcel>() {
@Override
public BackupKeyringParcel createFromParcel(Parcel in) {
return new BackupKeyringParcel(in);
}
@Override
public BackupKeyringParcel[] newArray(int size) {
return new BackupKeyringParcel[size];
}
};
} }
@@ -19,36 +19,16 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.google.auto.value.AutoValue;
public class BenchmarkInputParcel implements Parcelable {
public BenchmarkInputParcel() { @AutoValue
public abstract class BenchmarkInputParcel implements Parcelable {
public static BenchmarkInputParcel newInstance() {
return new AutoValue_BenchmarkInputParcel();
} }
protected BenchmarkInputParcel(Parcel in) {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Creator<BenchmarkInputParcel> CREATOR = new Creator<BenchmarkInputParcel>() {
@Override
public BenchmarkInputParcel createFromParcel(Parcel in) {
return new BenchmarkInputParcel(in);
}
@Override
public BenchmarkInputParcel[] newArray(int size) {
return new BenchmarkInputParcel[size];
}
};
} }
@@ -18,99 +18,80 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import android.os.Parcelable;
import android.support.annotation.CheckResult;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
/** @AutoValue
* This class is a a transferable representation for a number of keyrings to public abstract class CertifyActionsParcel implements Parcelable {
* be certified. public abstract long getMasterKeyId();
*/ public abstract ArrayList<CertifyAction> getCertifyActions();
public class CertifyActionsParcel implements Parcelable { @Nullable
public abstract ParcelableHkpKeyserver getParcelableKeyServer();
// the master key id to certify with public static Builder builder(long masterKeyId) {
final public long mMasterKeyId; return new AutoValue_CertifyActionsParcel.Builder()
public CertifyLevel mLevel; .setMasterKeyId(masterKeyId)
.setCertifyActions(new ArrayList<CertifyAction>());
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
public ParcelableHkpKeyserver keyServerUri;
public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId;
mLevel = CertifyLevel.DEFAULT;
} }
public CertifyActionsParcel(Parcel source) { @AutoValue.Builder
mMasterKeyId = source.readLong(); public abstract static class Builder {
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable! abstract Builder setMasterKeyId(long masterKeyId);
mLevel = CertifyLevel.values()[source.readInt()]; public abstract Builder setCertifyActions(ArrayList<CertifyAction> certifyActions);
keyServerUri = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); public abstract Builder setParcelableKeyServer(ParcelableHkpKeyserver uri);
mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable(); abstract ArrayList<CertifyAction> getCertifyActions();
public void addAction(CertifyAction action) {
getCertifyActions().add(action);
}
public void addActions(Collection<CertifyAction> certifyActions) {
getCertifyActions().addAll(certifyActions);
} }
public void add(CertifyAction action) { public abstract CertifyActionsParcel build();
mCertifyActions.add(action);
} }
@Override @AutoValue
public void writeToParcel(Parcel destination, int flags) { public abstract static class CertifyAction implements Parcelable {
destination.writeLong(mMasterKeyId); public abstract long getMasterKeyId();
destination.writeInt(mLevel.ordinal()); @Nullable
destination.writeParcelable(keyServerUri, flags); public abstract ArrayList<String> getUserIds();
@Nullable
public abstract ArrayList<WrappedUserAttribute> getUserAttributes();
destination.writeSerializable(mCertifyActions); public static CertifyAction createForUserIds(long masterKeyId, List<String> userIds) {
return new AutoValue_CertifyActionsParcel_CertifyAction(masterKeyId, new ArrayList<>(userIds), null);
} }
public static final Creator<CertifyActionsParcel> CREATOR = new Creator<CertifyActionsParcel>() { public static CertifyAction createForUserAttributes(long masterKeyId, List<WrappedUserAttribute> attributes) {
public CertifyActionsParcel createFromParcel(final Parcel source) { return new AutoValue_CertifyActionsParcel_CertifyAction(masterKeyId, null, new ArrayList<>(attributes));
return new CertifyActionsParcel(source);
} }
public CertifyActionsParcel[] newArray(final int size) { @CheckResult
return new CertifyActionsParcel[size]; public CertifyAction withAddedUserIds(ArrayList<String> addedUserIds) {
} if (getUserAttributes() != null) {
}; throw new IllegalStateException("Can't add user ids to user attribute certification parcel!");
// TODO make this parcelable
public static class CertifyAction implements Serializable {
final public long mMasterKeyId;
final public ArrayList<String> mUserIds;
final public ArrayList<WrappedUserAttribute> mUserAttributes;
public CertifyAction(long masterKeyId, List<String> userIds, List<WrappedUserAttribute> attributes) {
mMasterKeyId = masterKeyId;
mUserIds = userIds == null ? null : new ArrayList<>(userIds);
mUserAttributes = attributes == null ? null : new ArrayList<>(attributes);
} }
ArrayList<String> prevUserIds = getUserIds();
if (prevUserIds == null) {
throw new IllegalStateException("Can't add user ids to user attribute certification parcel!");
} }
@Override ArrayList<String> userIds = new ArrayList<>(prevUserIds);
public int describeContents() { userIds.addAll(addedUserIds);
return 0; return new AutoValue_CertifyActionsParcel_CertifyAction(getMasterKeyId(), userIds, null);
} }
@Override
public String toString() {
String out = "mMasterKeyId: " + mMasterKeyId + "\n";
out += "mLevel: " + mLevel + "\n";
out += "mCertifyActions: " + mCertifyActions + "\n";
return out;
} }
// All supported algorithms
public enum CertifyLevel {
DEFAULT, NONE, CASUAL, POSITIVE
}
} }
@@ -19,70 +19,29 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
public class ChangeUnlockParcel implements Parcelable { @AutoValue
public abstract class ChangeUnlockParcel implements Parcelable {
@Nullable
public abstract Long getMasterKeyId();
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getFingerprint();
public abstract Passphrase getNewPassphrase();
// the master key id of keyring.
public Long mMasterKeyId;
// the key fingerprint, for safety.
public byte[] mFingerprint;
// The new passphrase to use
public final Passphrase mNewPassphrase;
public ChangeUnlockParcel(Passphrase newPassphrase) { public static ChangeUnlockParcel createChangeUnlockParcel(Long masterKeyId, byte[] fingerprint,
mNewPassphrase = newPassphrase; Passphrase newPassphrase) {
return new AutoValue_ChangeUnlockParcel(masterKeyId, fingerprint, newPassphrase);
} }
public ChangeUnlockParcel(Long masterKeyId, byte[] fingerprint, Passphrase newPassphrase) { public static ChangeUnlockParcel createUnLockParcelForNewKey(Passphrase newPassphrase) {
if (newPassphrase == null) { return new AutoValue_ChangeUnlockParcel(null, null, newPassphrase);
throw new AssertionError("newPassphrase must be non-null. THIS IS A BUG!");
} }
mMasterKeyId = masterKeyId;
mFingerprint = fingerprint;
mNewPassphrase = newPassphrase;
}
public ChangeUnlockParcel(Parcel source) {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeInt(mMasterKeyId == null ? 0 : 1);
if (mMasterKeyId != null) {
destination.writeLong(mMasterKeyId);
}
destination.writeByteArray(mFingerprint);
destination.writeParcelable(mNewPassphrase, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<ChangeUnlockParcel> CREATOR = new Creator<ChangeUnlockParcel>() {
public ChangeUnlockParcel createFromParcel(final Parcel source) {
return new ChangeUnlockParcel(source);
}
public ChangeUnlockParcel[] newArray(final int size) {
return new ChangeUnlockParcel[size];
}
};
public String toString() {
String out = "mMasterKeyId: " + mMasterKeyId + "\n";
out += "passphrase (" + mNewPassphrase + ")";
return out;
}
} }
@@ -19,40 +19,17 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
public class ConsolidateInputParcel implements Parcelable { import com.google.auto.value.AutoValue;
public boolean mConsolidateRecovery;
public ConsolidateInputParcel(boolean consolidateRecovery) { @AutoValue
mConsolidateRecovery = consolidateRecovery; public abstract class ConsolidateInputParcel implements Parcelable {
public abstract boolean isStartFromRecovery();
public static ConsolidateInputParcel createConsolidateInputParcel(boolean consolidateRecovery) {
return new AutoValue_ConsolidateInputParcel(consolidateRecovery);
} }
protected ConsolidateInputParcel(Parcel in) {
mConsolidateRecovery = in.readByte() != 0x00;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
}
public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
@Override
public ConsolidateInputParcel createFromParcel(Parcel in) {
return new ConsolidateInputParcel(in);
}
@Override
public ConsolidateInputParcel[] newArray(int size) {
return new ConsolidateInputParcel[size];
}
};
} }
@@ -19,45 +19,25 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
public class DeleteKeyringParcel implements Parcelable { import com.google.auto.value.AutoValue;
public long[] mMasterKeyIds;
public boolean mIsSecret;
public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) { @AutoValue
mMasterKeyIds = masterKeyIds; public abstract class DeleteKeyringParcel implements Parcelable {
mIsSecret = isSecret; @SuppressWarnings("mutable")
public abstract long[] getMasterKeyIds();
public abstract boolean isDeleteSecret();
public static DeleteKeyringParcel createDeletePublicKeysParcel(long[] masterKeyIds) {
return new AutoValue_DeleteKeyringParcel(masterKeyIds, false);
} }
protected DeleteKeyringParcel(Parcel in) { public static DeleteKeyringParcel createDeleteSingleSecretKeyParcel(long masterKeyId) {
mIsSecret = in.readByte() != 0x00; return new AutoValue_DeleteKeyringParcel(new long[] { masterKeyId }, true);
mMasterKeyIds = in.createLongArray();
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
dest.writeLongArray(mMasterKeyIds);
}
public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
@Override
public DeleteKeyringParcel createFromParcel(Parcel in) {
return new DeleteKeyringParcel(in);
}
@Override
public DeleteKeyringParcel[] newArray(int size) {
return new DeleteKeyringParcel[size];
}
};
} }
@@ -18,69 +18,31 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import java.util.ArrayList; import java.util.ArrayList;
public class ImportKeyringParcel implements Parcelable { import android.os.Parcelable;
// If null, keys are expected to be read from a cache file in ImportExportOperations import android.support.annotation.Nullable;
public ArrayList<ParcelableKeyRing> mKeyList;
public ParcelableHkpKeyserver mKeyserver; // must be set if keys are to be imported from a keyserver
// If false, don't save the key, only return it as part of result import com.google.auto.value.AutoValue;
public boolean mSkipSave = false; import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver) { @AutoValue
mKeyList = keyList; public abstract class ImportKeyringParcel implements Parcelable {
mKeyserver = keyserver; @Nullable // If null, keys are expected to be read from a cache file in ImportExportOperations
public abstract ArrayList<ParcelableKeyRing> getKeyList();
@Nullable // must be set if keys are to be imported from a keyserver
public abstract ParcelableHkpKeyserver getKeyserver();
public abstract boolean isSkipSave();
public static ImportKeyringParcel createImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList,
ParcelableHkpKeyserver keyserver) {
return new AutoValue_ImportKeyringParcel(keyList, keyserver, false);
} }
public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver, boolean skipSave) { public static ImportKeyringParcel createWithSkipSave(ArrayList<ParcelableKeyRing> keyList,
this(keyList, keyserver); ParcelableHkpKeyserver keyserver) {
mSkipSave = skipSave; return new AutoValue_ImportKeyringParcel(keyList, keyserver, true);
} }
protected ImportKeyringParcel(Parcel in) {
if (in.readByte() == 0x01) {
mKeyList = new ArrayList<>();
in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
} else {
mKeyList = null;
}
mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
mSkipSave = in.readInt() != 0;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mKeyList == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(mKeyList);
}
dest.writeParcelable(mKeyserver, flags);
dest.writeInt(mSkipSave ? 1 : 0);
}
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
@Override
public ImportKeyringParcel createFromParcel(Parcel in) {
return new ImportKeyringParcel(in);
}
@Override
public ImportKeyringParcel[] newArray(int size) {
return new ImportKeyringParcel[size];
}
};
} }
@@ -17,65 +17,24 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.net.Uri;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
public class InputDataParcel implements Parcelable { @AutoValue
public abstract class InputDataParcel implements Parcelable {
public abstract Uri getInputUri();
@Nullable
public abstract PgpDecryptVerifyInputParcel getDecryptInput();
public abstract boolean getMimeDecode(); // TODO static value - ditch this?
private Uri mInputUri; public static InputDataParcel createInputDataParcel(Uri inputUri, PgpDecryptVerifyInputParcel decryptInput) {
return new AutoValue_InputDataParcel(inputUri, decryptInput, true);
private PgpDecryptVerifyInputParcel mDecryptInput;
private boolean mMimeDecode = true; // TODO default to false
public InputDataParcel(Uri inputUri, PgpDecryptVerifyInputParcel decryptInput) {
mInputUri = inputUri;
mDecryptInput = decryptInput;
} }
InputDataParcel(Parcel source) {
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mInputUri = source.readParcelable(getClass().getClassLoader());
mDecryptInput = source.readParcelable(getClass().getClassLoader());
mMimeDecode = source.readInt() != 0;
}
public Uri getInputUri() {
return mInputUri;
}
public PgpDecryptVerifyInputParcel getDecryptInput() {
return mDecryptInput;
}
public boolean getMimeDecode() {
return mMimeDecode;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mInputUri, 0);
dest.writeParcelable(mDecryptInput, 0);
dest.writeInt(mMimeDecode ? 1 : 0);
}
public static final Creator<InputDataParcel> CREATOR = new Creator<InputDataParcel>() {
public InputDataParcel createFromParcel(final Parcel source) {
return new InputDataParcel(source);
}
public InputDataParcel[] newArray(final int size) {
return new InputDataParcel[size];
}
};
} }
@@ -110,7 +110,7 @@ public class KeyserverSyncAdapterService extends Service {
} }
case ACTION_UPDATE_ALL: { case ACTION_UPDATE_ALL: {
// does not check for screen on/off // does not check for screen on/off
asyncKeyUpdate(this, new CryptoInputParcel(), startId); asyncKeyUpdate(this, CryptoInputParcel.createCryptoInputParcel(), startId);
// we depend on handleUpdateResult to call stopSelf when it is no longer necessary // we depend on handleUpdateResult to call stopSelf when it is no longer necessary
// for the intent to be redelivered // for the intent to be redelivered
return START_REDELIVER_INTENT; return START_REDELIVER_INTENT;
@@ -118,7 +118,7 @@ public class KeyserverSyncAdapterService extends Service {
case ACTION_IGNORE_TOR: { case ACTION_IGNORE_TOR: {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy()), asyncKeyUpdate(this, CryptoInputParcel.createCryptoInputParcel(ParcelableProxy.getForNoProxy()),
startId); startId);
// we depend on handleUpdateResult to call stopSelf when it is no longer necessary // we depend on handleUpdateResult to call stopSelf when it is no longer necessary
// for the intent to be redelivered // for the intent to be redelivered
@@ -324,7 +324,7 @@ public class KeyserverSyncAdapterService extends Service {
ImportOperation importOp = new ImportOperation(context, ImportOperation importOp = new ImportOperation(context,
KeyWritableRepository.createDatabaseReadWriteInteractor(context), null); KeyWritableRepository.createDatabaseReadWriteInteractor(context), null);
return importOp.execute( return importOp.execute(
new ImportKeyringParcel(keyList, ImportKeyringParcel.createImportKeyringParcel(keyList,
Preferences.getPreferences(context).getPreferredKeyserver()), Preferences.getPreferences(context).getPreferredKeyserver()),
cryptoInputParcel cryptoInputParcel
); );
@@ -384,7 +384,7 @@ public class KeyserverSyncAdapterService extends Service {
ImportKeyResult result = ImportKeyResult result =
new ImportOperation(context, KeyWritableRepository.createDatabaseReadWriteInteractor(context), null, mCancelled) new ImportOperation(context, KeyWritableRepository.createDatabaseReadWriteInteractor(context), null, mCancelled)
.execute( .execute(
new ImportKeyringParcel( ImportKeyringParcel.createImportKeyringParcel(
keyWrapper, keyWrapper,
Preferences.getPreferences(context) Preferences.getPreferences(context)
.getPreferredKeyserver() .getPreferredKeyserver()
@@ -19,48 +19,25 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
public class PromoteKeyringParcel implements Parcelable { import com.google.auto.value.AutoValue;
public long mKeyRingId;
public byte[] mCardAid;
public long[] mSubKeyIds;
public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) { @AutoValue
mKeyRingId = keyRingId; public abstract class PromoteKeyringParcel implements Parcelable {
mCardAid = cardAid; public abstract long getMasterKeyId();
mSubKeyIds = subKeyIds; @Nullable
@SuppressWarnings("mutable")
public abstract byte[] getCardAid();
@Nullable
@SuppressWarnings("mutable")
public abstract long[] getSubKeyIds();
public static PromoteKeyringParcel createPromoteKeyringParcel(long keyRingId, byte[] cardAid,
@Nullable long[] subKeyIds) {
return new AutoValue_PromoteKeyringParcel(keyRingId, cardAid, subKeyIds);
} }
protected PromoteKeyringParcel(Parcel in) {
mKeyRingId = in.readLong();
mCardAid = in.createByteArray();
mSubKeyIds = in.createLongArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mKeyRingId);
dest.writeByteArray(mCardAid);
dest.writeLongArray(mSubKeyIds);
}
public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
@Override
public PromoteKeyringParcel createFromParcel(Parcel in) {
return new PromoteKeyringParcel(in);
}
@Override
public PromoteKeyringParcel[] newArray(int size) {
return new PromoteKeyringParcel[size];
}
};
} }
@@ -19,50 +19,22 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
public class RevokeKeyringParcel implements Parcelable { @AutoValue
public abstract class RevokeKeyringParcel implements Parcelable {
public abstract long getMasterKeyId();
public abstract boolean isShouldUpload();
@Nullable
public abstract ParcelableHkpKeyserver getKeyserver();
final public long mMasterKeyId; public static RevokeKeyringParcel createRevokeKeyringParcel(long masterKeyId, boolean upload,
final public boolean mUpload; ParcelableHkpKeyserver keyserver) {
final public ParcelableHkpKeyserver mKeyserver; return new AutoValue_RevokeKeyringParcel(masterKeyId, upload, keyserver);
public RevokeKeyringParcel(long masterKeyId, boolean upload, ParcelableHkpKeyserver keyserver) {
mMasterKeyId = masterKeyId;
mUpload = upload;
mKeyserver = keyserver;
} }
protected RevokeKeyringParcel(Parcel in) {
mMasterKeyId = in.readLong();
mUpload = in.readByte() != 0x00;
mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mMasterKeyId);
dest.writeByte((byte) (mUpload ? 0x01 : 0x00));
dest.writeParcelable(mKeyserver, flags);
}
public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() {
@Override
public RevokeKeyringParcel createFromParcel(Parcel in) {
return new RevokeKeyringParcel(in);
}
@Override
public RevokeKeyringParcel[] newArray(int size) {
return new RevokeKeyringParcel[size];
}
};
} }
@@ -18,15 +18,19 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Passphrase;
/** /**
* This class is a a transferable representation for a collection of changes * This class is a a transferable representation for a collection of changes
@@ -43,304 +47,245 @@ import java.util.ArrayList;
* error in any included operation (for example revocation of a non-existent * error in any included operation (for example revocation of a non-existent
* subkey) will cause the operation as a whole to fail. * subkey) will cause the operation as a whole to fail.
*/ */
public class SaveKeyringParcel implements Parcelable { @AutoValue
public abstract class SaveKeyringParcel implements Parcelable {
// the master key id to be edited. if this is null, a new one will be created // the master key id to be edited. if this is null, a new one will be created
public Long mMasterKeyId; @Nullable
public abstract Long getMasterKeyId();
// the key fingerprint, for safety. MUST be null for a new key. // the key fingerprint, for safety. MUST be null for a new key.
public byte[] mFingerprint; @Nullable
@SuppressWarnings("mutable")
public abstract byte[] getFingerprint();
public ArrayList<String> mAddUserIds; public abstract List<String> getAddUserIds();
public ArrayList<WrappedUserAttribute> mAddUserAttribute; public abstract List<WrappedUserAttribute> getAddUserAttribute();
public ArrayList<SubkeyAdd> mAddSubKeys; public abstract List<SubkeyAdd> getAddSubKeys();
public ArrayList<SubkeyChange> mChangeSubKeys; public abstract List<SubkeyChange> getChangeSubKeys();
public String mChangePrimaryUserId; @Nullable
public abstract String getChangePrimaryUserId();
public ArrayList<String> mRevokeUserIds; public abstract List<String> getRevokeUserIds();
public ArrayList<Long> mRevokeSubKeys; public abstract List<Long> getRevokeSubKeys();
// if these are non-null, PINs will be changed on the token // if these are non-null, PINs will be changed on the token
public Passphrase mSecurityTokenPin; @Nullable
public Passphrase mSecurityTokenAdminPin; public abstract Passphrase getSecurityTokenPin();
@Nullable
public abstract Passphrase getSecurityTokenAdminPin();
// private because they have to be set together with setUpdateOptions public abstract boolean isShouldUpload();
private boolean mUpload; public abstract boolean isShouldUploadAtomic();
private boolean mUploadAtomic; @Nullable
private ParcelableHkpKeyserver mKeyserver; public abstract ParcelableHkpKeyserver getUploadKeyserver();
// private because we have to set other details like key id @Nullable
private ChangeUnlockParcel mNewUnlock; public abstract ChangeUnlockParcel getNewUnlock();
public SaveKeyringParcel() { public static Builder buildNewKeyringParcel() {
reset(); return new AutoValue_SaveKeyringParcel.Builder()
.setShouldUpload(false)
.setShouldUploadAtomic(false);
} }
public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) { public static Builder buildChangeKeyringParcel(long masterKeyId, byte[] fingerprint) {
this(); return buildNewKeyringParcel()
mMasterKeyId = masterKeyId; .setMasterKeyId(masterKeyId)
mFingerprint = fingerprint; .setFingerprint(fingerprint);
} }
public void reset() { abstract Builder toBuilder();
mNewUnlock = null;
mAddUserIds = new ArrayList<>(); public static Builder buildUpon(SaveKeyringParcel saveKeyringParcel) {
mAddUserAttribute = new ArrayList<>(); SaveKeyringParcel.Builder builder = saveKeyringParcel.toBuilder();
mAddSubKeys = new ArrayList<>(); builder.addUserIds.addAll(saveKeyringParcel.getAddUserIds());
mChangePrimaryUserId = null; builder.revokeUserIds.addAll(saveKeyringParcel.getRevokeUserIds());
mChangeSubKeys = new ArrayList<>(); builder.addUserAttribute.addAll(saveKeyringParcel.getAddUserAttribute());
mRevokeUserIds = new ArrayList<>(); builder.addSubKeys.addAll(saveKeyringParcel.getAddSubKeys());
mRevokeSubKeys = new ArrayList<>(); builder.changeSubKeys.addAll(saveKeyringParcel.getChangeSubKeys());
mSecurityTokenPin = null; builder.revokeSubKeys.addAll(saveKeyringParcel.getRevokeSubKeys());
mSecurityTokenAdminPin = null; return builder;
mUpload = false;
mUploadAtomic = false;
mKeyserver = null;
} }
@AutoValue.Builder
public static abstract class Builder {
private ArrayList<String> addUserIds = new ArrayList<>();
private ArrayList<String> revokeUserIds = new ArrayList<>();
private ArrayList<WrappedUserAttribute> addUserAttribute = new ArrayList<>();
private ArrayList<SubkeyAdd> addSubKeys = new ArrayList<>();
private ArrayList<SubkeyChange> changeSubKeys = new ArrayList<>();
private ArrayList<Long> revokeSubKeys = new ArrayList<>();
public abstract Builder setChangePrimaryUserId(String changePrimaryUserId);
public abstract Builder setSecurityTokenPin(Passphrase securityTokenPin);
public abstract Builder setSecurityTokenAdminPin(Passphrase securityTokenAdminPin);
public abstract Builder setNewUnlock(ChangeUnlockParcel newUnlock);
public abstract Long getMasterKeyId();
public abstract byte[] getFingerprint();
public abstract String getChangePrimaryUserId();
public ArrayList<SubkeyAdd> getMutableAddSubKeys() {
return addSubKeys;
}
public ArrayList<String> getMutableAddUserIds() {
return addUserIds;
}
public ArrayList<Long> getMutableRevokeSubKeys() {
return revokeSubKeys;
}
public ArrayList<String> getMutableRevokeUserIds() {
return revokeUserIds;
}
abstract Builder setMasterKeyId(Long masterKeyId);
abstract Builder setFingerprint(byte[] fingerprint);
abstract Builder setAddUserIds(List<String> addUserIds);
abstract Builder setAddUserAttribute(List<WrappedUserAttribute> addUserAttribute);
abstract Builder setAddSubKeys(List<SubkeyAdd> addSubKeys);
abstract Builder setChangeSubKeys(List<SubkeyChange> changeSubKeys);
abstract Builder setRevokeUserIds(List<String> revokeUserIds);
abstract Builder setRevokeSubKeys(List<Long> revokeSubKeys);
abstract Builder setShouldUpload(boolean upload);
abstract Builder setShouldUploadAtomic(boolean uploadAtomic);
abstract Builder setUploadKeyserver(ParcelableHkpKeyserver keyserver);
public void setUpdateOptions(boolean upload, boolean uploadAtomic, ParcelableHkpKeyserver keyserver) { public void setUpdateOptions(boolean upload, boolean uploadAtomic, ParcelableHkpKeyserver keyserver) {
mUpload = upload; setShouldUpload(upload);
mUploadAtomic = uploadAtomic; setShouldUploadAtomic(uploadAtomic);
mKeyserver = keyserver; setUploadKeyserver(keyserver);
} }
public void setNewUnlock(ChangeUnlockParcel parcel) { public void addSubkeyAdd(SubkeyAdd subkeyAdd) {
mNewUnlock = parcel; addSubKeys.add(subkeyAdd);
} }
public ChangeUnlockParcel getChangeUnlockParcel() { public void addUserId(String userId) {
if(mNewUnlock != null) { addUserIds.add(userId);
mNewUnlock.mMasterKeyId = mMasterKeyId;
mNewUnlock.mFingerprint = mFingerprint;
}
return mNewUnlock;
} }
public boolean isUpload() { public void addRevokeSubkey(long masterKeyId) {
return mUpload; revokeSubKeys.add(masterKeyId);
} }
public boolean isUploadAtomic() { public void removeRevokeSubkey(long keyId) {
return mUploadAtomic; revokeSubKeys.remove(keyId);
} }
public ParcelableHkpKeyserver getUploadKeyserver() { public void addRevokeUserId(String userId) {
return mKeyserver; revokeUserIds.add(userId);
} }
public boolean isEmpty() { public void removeRevokeUserId(String userId) {
return isRestrictedOnly() && mChangeSubKeys.isEmpty(); revokeUserIds.remove(userId);
} }
/** Returns true iff this parcel does not contain any operations which require a passphrase. */ public void addOrReplaceSubkeyChange(SubkeyChange newChange) {
public boolean isRestrictedOnly() { SubkeyChange foundSubkeyChange = getSubkeyChange(newChange.getSubKeyId());
if (mNewUnlock != null || !mAddUserIds.isEmpty() || !mAddUserAttribute.isEmpty()
|| !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeUserIds.isEmpty() if (foundSubkeyChange != null) {
|| !mRevokeSubKeys.isEmpty()) { changeSubKeys.remove(foundSubkeyChange);
return false; }
changeSubKeys.add(newChange);
} }
for (SubkeyChange change : mChangeSubKeys) { public void removeSubkeyChange(SubkeyChange change) {
if (change.mRecertify || change.mFlags != null || change.mExpiry != null changeSubKeys.remove(change);
|| change.mMoveKeyToSecurityToken) {
return false;
}
}
return true;
}
// performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
public static class SubkeyAdd implements Serializable {
public Algorithm mAlgorithm;
public Integer mKeySize;
public Curve mCurve;
public int mFlags;
public Long mExpiry;
public SubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags, Long expiry) {
mAlgorithm = algorithm;
mKeySize = keySize;
mCurve = curve;
mFlags = flags;
mExpiry = expiry;
}
@Override
public String toString() {
String out = "mAlgorithm: " + mAlgorithm + ", ";
out += "mKeySize: " + mKeySize + ", ";
out += "mCurve: " + mCurve + ", ";
out += "mFlags: " + mFlags;
out += "mExpiry: " + mExpiry;
return out;
}
}
public static class SubkeyChange implements Serializable {
public final long mKeyId;
public Integer mFlags;
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
public Long mExpiry;
// if this flag is true, the key will be recertified even if all above
// values are no-ops
public boolean mRecertify;
// if this flag is true, the subkey should be changed to a stripped key
public boolean mDummyStrip;
// if this flag is true, the subkey should be moved to a security token
public boolean mMoveKeyToSecurityToken;
// if this is non-null, the subkey will be changed to a divert-to-card
// (security token) key for the given serial number
public byte[] mSecurityTokenSerialNo;
public SubkeyChange(long keyId) {
mKeyId = keyId;
}
public SubkeyChange(long keyId, boolean recertify) {
mKeyId = keyId;
mRecertify = recertify;
}
public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
mFlags = flags;
mExpiry = expiry;
}
public SubkeyChange(long keyId, boolean dummyStrip, boolean moveKeyToSecurityToken) {
this(keyId, null, null);
// these flags are mutually exclusive!
if (dummyStrip && moveKeyToSecurityToken) {
throw new AssertionError(
"cannot set strip and moveKeyToSecurityToken" +
" flags at the same time - this is a bug!");
}
mDummyStrip = dummyStrip;
mMoveKeyToSecurityToken = moveKeyToSecurityToken;
}
@Override
public String toString() {
String out = "mKeyId: " + mKeyId + ", ";
out += "mFlags: " + mFlags + ", ";
out += "mExpiry: " + mExpiry + ", ";
out += "mDummyStrip: " + mDummyStrip + ", ";
out += "mMoveKeyToSecurityToken: " + mMoveKeyToSecurityToken + ", ";
out += "mSecurityTokenSerialNo: [" + (mSecurityTokenSerialNo == null ? 0 : mSecurityTokenSerialNo.length) + " bytes]";
return out;
}
} }
public SubkeyChange getSubkeyChange(long keyId) { public SubkeyChange getSubkeyChange(long keyId) {
for (SubkeyChange subkeyChange : mChangeSubKeys) { if (changeSubKeys == null) {
if (subkeyChange.mKeyId == keyId) { return null;
}
for (SubkeyChange subkeyChange : changeSubKeys) {
if (subkeyChange.getSubKeyId() == keyId) {
return subkeyChange; return subkeyChange;
} }
} }
return null; return null;
} }
public SubkeyChange getOrCreateSubkeyChange(long keyId) { public void addUserAttribute(WrappedUserAttribute ua) {
SubkeyChange foundSubkeyChange = getSubkeyChange(keyId); addUserAttribute.add(ua);
if (foundSubkeyChange != null) { }
return foundSubkeyChange;
} else { abstract SaveKeyringParcel autoBuild();
// else, create a new one
SubkeyChange newSubkeyChange = new SubkeyChange(keyId); public SaveKeyringParcel build() {
mChangeSubKeys.add(newSubkeyChange); setAddUserAttribute(Collections.unmodifiableList(addUserAttribute));
return newSubkeyChange; setRevokeSubKeys(Collections.unmodifiableList(revokeSubKeys));
setRevokeUserIds(Collections.unmodifiableList(revokeUserIds));
setAddSubKeys(Collections.unmodifiableList(addSubKeys));
setAddUserIds(Collections.unmodifiableList(addUserIds));
setChangeSubKeys(Collections.unmodifiableList(changeSubKeys));
return autoBuild();
} }
} }
@SuppressWarnings("unchecked") // we verify the reads against writes in writeToParcel // performance gain for using Parcelable here would probably be negligible,
public SaveKeyringParcel(Parcel source) { // use Serializable instead.
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null; @AutoValue
mFingerprint = source.createByteArray(); public abstract static class SubkeyAdd implements Serializable {
public abstract Algorithm getAlgorithm();
@Nullable
public abstract Integer getKeySize();
@Nullable
public abstract Curve getCurve();
public abstract int getFlags();
@Nullable
public abstract Long getExpiry();
mNewUnlock = source.readParcelable(getClass().getClassLoader()); public static SubkeyAdd createSubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags,
Long expiry) {
mAddUserIds = source.createStringArrayList(); return new AutoValue_SaveKeyringParcel_SubkeyAdd(algorithm, keySize, curve, flags, expiry);
mAddUserAttribute = (ArrayList<WrappedUserAttribute>) source.readSerializable(); }
mAddSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable();
mChangeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable();
mChangePrimaryUserId = source.readString();
mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
mSecurityTokenPin = source.readParcelable(Passphrase.class.getClassLoader());
mSecurityTokenAdminPin = source.readParcelable(Passphrase.class.getClassLoader());
mUpload = source.readByte() != 0;
mUploadAtomic = source.readByte() != 0;
mKeyserver = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
} }
@Override @AutoValue
public void writeToParcel(Parcel destination, int flags) { public abstract static class SubkeyChange implements Serializable {
destination.writeInt(mMasterKeyId == null ? 0 : 1); public abstract long getSubKeyId();
if (mMasterKeyId != null) { @Nullable
destination.writeLong(mMasterKeyId); public abstract Integer getFlags();
} // this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
destination.writeByteArray(mFingerprint); @Nullable
public abstract Long getExpiry();
// if this flag is true, the key will be recertified even if all above
// values are no-ops
public abstract boolean getRecertify();
// if this flag is true, the subkey should be changed to a stripped key
public abstract boolean getDummyStrip();
// if this flag is true, the subkey should be moved to a security token
public abstract boolean getMoveKeyToSecurityToken();
// if this is non-null, the subkey will be changed to a divert-to-card
// (security token) key for the given serial number
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getSecurityTokenSerialNo();
// yes, null values are ok for parcelables public static SubkeyChange createRecertifyChange(long keyId, boolean recertify) {
destination.writeParcelable(mNewUnlock, flags); return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, recertify, false, false, null);
destination.writeStringList(mAddUserIds);
destination.writeSerializable(mAddUserAttribute);
destination.writeSerializable(mAddSubKeys);
destination.writeSerializable(mChangeSubKeys);
destination.writeString(mChangePrimaryUserId);
destination.writeStringList(mRevokeUserIds);
destination.writeSerializable(mRevokeSubKeys);
destination.writeParcelable(mSecurityTokenPin, flags);
destination.writeParcelable(mSecurityTokenAdminPin, flags);
destination.writeByte((byte) (mUpload ? 1 : 0));
destination.writeByte((byte) (mUploadAtomic ? 1 : 0));
destination.writeParcelable(mKeyserver, flags);
} }
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { public static SubkeyChange createFlagsOrExpiryChange(long keyId, Integer flags, Long expiry) {
public SaveKeyringParcel createFromParcel(final Parcel source) { return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, flags, expiry, false, false, false, null);
return new SaveKeyringParcel(source);
} }
public SaveKeyringParcel[] newArray(final int size) { public static SubkeyChange createStripChange(long keyId) {
return new SaveKeyringParcel[size]; return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, true, false, null);
}
};
@Override
public int describeContents() {
return 0;
} }
@Override public static SubkeyChange createMoveToSecurityTokenChange(long keyId) {
public String toString() { return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, false, true, null);
String out = "mMasterKeyId: " + mMasterKeyId + "\n"; }
out += "mNewUnlock: " + mNewUnlock + "\n";
out += "mAddUserIds: " + mAddUserIds + "\n";
out += "mAddUserAttribute: " + mAddUserAttribute + "\n";
out += "mAddSubKeys: " + mAddSubKeys + "\n";
out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
out += "mSecurityTokenPin: " + mSecurityTokenPin + "\n";
out += "mSecurityTokenAdminPin: " + mSecurityTokenAdminPin;
return out; public static SubkeyChange createSecurityTokenSerialNo(long keyId, byte[] securityTokenSerialNo) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, false, false, securityTokenSerialNo);
}
} }
// All supported algorithms // All supported algorithms
@@ -357,7 +302,4 @@ public class SaveKeyringParcel implements Parcelable {
// (adding support would be trivial though -> JcaPGPKeyConverter.java:190) // (adding support would be trivial though -> JcaPGPKeyConverter.java:190)
// BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512 // BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512
} }
} }
@@ -20,62 +20,29 @@
package org.sufficientlysecure.keychain.service; package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
@AutoValue
public abstract class UploadKeyringParcel implements Parcelable {
public abstract ParcelableHkpKeyserver getKeyserver();
@Nullable
public abstract Long getMasterKeyId();
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getUncachedKeyringBytes();
public class UploadKeyringParcel implements Parcelable {
public ParcelableHkpKeyserver mKeyserver;
public final Long mMasterKeyId; public static UploadKeyringParcel createWithKeyId(ParcelableHkpKeyserver keyserver, long masterKeyId) {
public final byte[] mUncachedKeyringBytes; return new AutoValue_UploadKeyringParcel(keyserver, masterKeyId, null);
public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, long masterKeyId) {
mKeyserver = keyserver;
mMasterKeyId = masterKeyId;
mUncachedKeyringBytes = null;
} }
public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, byte[] uncachedKeyringBytes) { public static UploadKeyringParcel createWithKeyringBytes(ParcelableHkpKeyserver keyserver,
mKeyserver = keyserver; @NonNull byte[] uncachedKeyringBytes) {
mMasterKeyId = null; return new AutoValue_UploadKeyringParcel(keyserver, null, uncachedKeyringBytes);
mUncachedKeyringBytes = uncachedKeyringBytes;
} }
protected UploadKeyringParcel(Parcel in) {
mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
mMasterKeyId = in.readInt() != 0 ? in.readLong() : null;
mUncachedKeyringBytes = in.createByteArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mKeyserver, flags);
if (mMasterKeyId != null) {
dest.writeInt(1);
dest.writeLong(mMasterKeyId);
} else {
dest.writeInt(0);
}
dest.writeByteArray(mUncachedKeyringBytes);
}
public static final Creator<UploadKeyringParcel> CREATOR = new Creator<UploadKeyringParcel>() {
@Override
public UploadKeyringParcel createFromParcel(Parcel in) {
return new UploadKeyringParcel(in);
}
@Override
public UploadKeyringParcel[] newArray(int size) {
return new UploadKeyringParcel[size];
}
};
} }
@@ -19,181 +19,128 @@ package org.sufficientlysecure.keychain.service.input;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.CheckResult;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import com.ryanharter.auto.value.parcel.ParcelAdapter;
import org.sufficientlysecure.keychain.util.ByteMapParcelAdapter;
import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
/** @AutoValue
* This is a base class for the input of crypto operations. public abstract class CryptoInputParcel implements Parcelable {
*/ @Nullable
public class CryptoInputParcel implements Parcelable { public abstract Date getSignatureTime();
@Nullable
public abstract Passphrase getPassphrase();
public abstract boolean isCachePassphrase();
private Date mSignatureTime; public boolean hasPassphrase() {
private boolean mHasSignature; return getPassphrase() != null;
}
public Passphrase mPassphrase;
// used to supply an explicit proxy to operations that require it // used to supply an explicit proxy to operations that require it
// this is not final so it can be added to an existing CryptoInputParcel // this is not final so it can be added to an existing CryptoInputParcel
// (e.g) CertifyOperation with upload might require both passphrase and orbot to be enabled // (e.g) CertifyOperation with upload might require both passphrase and orbot to be enabled
private ParcelableProxy mParcelableProxy; @Nullable
public abstract ParcelableProxy getParcelableProxy();
// specifies whether passphrases should be cached
public boolean mCachePassphrase = true;
// this map contains both decrypted session keys and signed hashes to be // this map contains both decrypted session keys and signed hashes to be
// used in the crypto operation described by this parcel. // used in the crypto operation described by this parcel.
private HashMap<ByteBuffer, byte[]> mCryptoData = new HashMap<>(); @ParcelAdapter(ByteMapParcelAdapter.class)
public abstract Map<ByteBuffer, byte[]> getCryptoData();
public CryptoInputParcel() {
mSignatureTime = null; public static CryptoInputParcel createCryptoInputParcel() {
mPassphrase = null; return new AutoValue_CryptoInputParcel(null, null, true, null, Collections.<ByteBuffer,byte[]>emptyMap());
mCachePassphrase = true;
} }
public CryptoInputParcel(Date signatureTime, Passphrase passphrase) { public static CryptoInputParcel createCryptoInputParcel(Date signatureTime, Passphrase passphrase) {
mHasSignature = true; if (signatureTime == null) {
mSignatureTime = signatureTime == null ? new Date() : signatureTime; signatureTime = new Date();
mPassphrase = passphrase; }
mCachePassphrase = true; return new AutoValue_CryptoInputParcel(signatureTime, passphrase, true, null,
Collections.<ByteBuffer,byte[]>emptyMap());
} }
public CryptoInputParcel(Passphrase passphrase) { public static CryptoInputParcel createCryptoInputParcel(Passphrase passphrase) {
mPassphrase = passphrase; return new AutoValue_CryptoInputParcel(null, passphrase, true, null, Collections.<ByteBuffer,byte[]>emptyMap());
mCachePassphrase = true;
} }
public CryptoInputParcel(Date signatureTime) { public static CryptoInputParcel createCryptoInputParcel(Date signatureTime) {
mHasSignature = true; if (signatureTime == null) {
mSignatureTime = signatureTime == null ? new Date() : signatureTime; signatureTime = new Date();
mPassphrase = null; }
mCachePassphrase = true; return new AutoValue_CryptoInputParcel(signatureTime, null, true, null,
Collections.<ByteBuffer,byte[]>emptyMap());
} }
public CryptoInputParcel(ParcelableProxy parcelableProxy) { public static CryptoInputParcel createCryptoInputParcel(ParcelableProxy parcelableProxy) {
this(); return new AutoValue_CryptoInputParcel(null, null, true, parcelableProxy, new HashMap<ByteBuffer,byte[]>());
mParcelableProxy = parcelableProxy;
} }
public CryptoInputParcel(Date signatureTime, boolean cachePassphrase) { public static CryptoInputParcel createCryptoInputParcel(Date signatureTime, boolean cachePassphrase) {
mHasSignature = true; if (signatureTime == null) {
mSignatureTime = signatureTime == null ? new Date() : signatureTime; signatureTime = new Date();
mPassphrase = null; }
mCachePassphrase = cachePassphrase; return new AutoValue_CryptoInputParcel(signatureTime, null, cachePassphrase, null,
new HashMap<ByteBuffer,byte[]>());
} }
public CryptoInputParcel(boolean cachePassphrase) { public static CryptoInputParcel createCryptoInputParcel(boolean cachePassphrase) {
mCachePassphrase = cachePassphrase; return new AutoValue_CryptoInputParcel(null, null, cachePassphrase, null, new HashMap<ByteBuffer,byte[]>());
} }
protected CryptoInputParcel(Parcel source) { // TODO get rid of this!
mHasSignature = source.readByte() != 0; @CheckResult
if (mHasSignature) { public CryptoInputParcel withCryptoData(byte[] hash, byte[] signedHash) {
mSignatureTime = new Date(source.readLong()); Map<ByteBuffer,byte[]> newCryptoData = new HashMap<>(getCryptoData());
} newCryptoData.put(ByteBuffer.wrap(hash), signedHash);
mPassphrase = source.readParcelable(getClass().getClassLoader()); newCryptoData = Collections.unmodifiableMap(newCryptoData);
mParcelableProxy = source.readParcelable(getClass().getClassLoader());
mCachePassphrase = source.readByte() != 0;
{ return new AutoValue_CryptoInputParcel(getSignatureTime(), getPassphrase(), isCachePassphrase(),
int count = source.readInt(); getParcelableProxy(), newCryptoData);
mCryptoData = new HashMap<>(count);
for (int i = 0; i < count; i++) {
byte[] key = source.createByteArray();
byte[] value = source.createByteArray();
mCryptoData.put(ByteBuffer.wrap(key), value);
}
} }
@CheckResult
public CryptoInputParcel withCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
Map<ByteBuffer,byte[]> newCryptoData = new HashMap<>(getCryptoData());
newCryptoData.putAll(cachedSessionKeys);
newCryptoData = Collections.unmodifiableMap(newCryptoData);
return new AutoValue_CryptoInputParcel(getSignatureTime(), getPassphrase(), isCachePassphrase(),
getParcelableProxy(), newCryptoData);
} }
@Override
public int describeContents() { @CheckResult
return 0; public CryptoInputParcel withPassphrase(Passphrase passphrase) {
return new AutoValue_CryptoInputParcel(getSignatureTime(), passphrase, isCachePassphrase(),
getParcelableProxy(), getCryptoData());
} }
@Override @CheckResult
public void writeToParcel(Parcel dest, int flags) { public CryptoInputParcel withNoCachePassphrase() {
dest.writeByte((byte) (mHasSignature ? 1 : 0)); return new AutoValue_CryptoInputParcel(getSignatureTime(), getPassphrase(), false, getParcelableProxy(),
if (mHasSignature) { getCryptoData());
dest.writeLong(mSignatureTime.getTime());
}
dest.writeParcelable(mPassphrase, 0);
dest.writeParcelable(mParcelableProxy, 0);
dest.writeByte((byte) (mCachePassphrase ? 1 : 0));
dest.writeInt(mCryptoData.size());
for (HashMap.Entry<ByteBuffer, byte[]> entry : mCryptoData.entrySet()) {
dest.writeByteArray(entry.getKey().array());
dest.writeByteArray(entry.getValue());
}
} }
public void addParcelableProxy(ParcelableProxy parcelableProxy) { @CheckResult
mParcelableProxy = parcelableProxy; public CryptoInputParcel withSignatureTime(Date signatureTime) {
return new AutoValue_CryptoInputParcel(signatureTime, getPassphrase(), isCachePassphrase(),
getParcelableProxy(), getCryptoData());
} }
public void addSignatureTime(Date signatureTime) { @CheckResult
mSignatureTime = signatureTime; public CryptoInputParcel withParcelableProxy(ParcelableProxy parcelableProxy) {
return new AutoValue_CryptoInputParcel(getSignatureTime(), getPassphrase(), isCachePassphrase(),
parcelableProxy, getCryptoData());
} }
public void addCryptoData(byte[] hash, byte[] signedHash) {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}
public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
mCryptoData.putAll(cachedSessionKeys);
}
public ParcelableProxy getParcelableProxy() {
return mParcelableProxy;
}
public Map<ByteBuffer, byte[]> getCryptoData() {
return mCryptoData;
}
public Date getSignatureTime() {
return mSignatureTime;
}
public boolean hasPassphrase() {
return mPassphrase != null;
}
public Passphrase getPassphrase() {
return mPassphrase;
}
public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() {
public CryptoInputParcel createFromParcel(final Parcel source) {
return new CryptoInputParcel(source);
}
public CryptoInputParcel[] newArray(final int size) {
return new CryptoInputParcel[size];
}
};
@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();
}
} }
@@ -523,7 +523,8 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
// if we don't want to execute the actual operation outside of this activity, drop out here // if we don't want to execute the actual operation outside of this activity, drop out here
if (!mExecuteBackupOperation) { if (!mExecuteBackupOperation) {
((BackupActivity) getActivity()).handleBackupOperation(new CryptoInputParcel(passphrase)); ((BackupActivity) getActivity()).handleBackupOperation(
CryptoInputParcel.createCryptoInputParcel(passphrase));
return; return;
} }
@@ -531,7 +532,7 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
mCachedBackupUri = TemporaryFileProvider.createFile(activity, filename, mCachedBackupUri = TemporaryFileProvider.createFile(activity, filename,
Constants.MIME_TYPE_ENCRYPTED_ALTERNATE); Constants.MIME_TYPE_ENCRYPTED_ALTERNATE);
cryptoOperation(new CryptoInputParcel(passphrase)); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(passphrase));
return; return;
} }
@@ -605,7 +606,8 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
@Nullable @Nullable
@Override @Override
public BackupKeyringParcel createOperationInput() { public BackupKeyringParcel createOperationInput() {
return new BackupKeyringParcel(mMasterKeyIds, mExportSecret, true, true, mCachedBackupUri); return BackupKeyringParcel
.createBackupKeyringParcel(mMasterKeyIds, mExportSecret, true, true, mCachedBackupUri);
} }
@Override @Override
@@ -113,7 +113,7 @@ public class CertifyKeyFragment
Notify.create(getActivity(), getString(R.string.select_key_to_certify), Notify.create(getActivity(), getString(R.string.select_key_to_certify),
Notify.Style.ERROR).show(); Notify.Style.ERROR).show();
} else { } else {
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
} }
} }
}); });
@@ -140,18 +140,17 @@ public class CertifyKeyFragment
long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId(); long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId();
// fill values for this action // fill values for this action
CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId); CertifyActionsParcel.Builder actionsParcel = CertifyActionsParcel.builder(selectedKeyId);
actionsParcel.mCertifyActions.addAll(certifyActions); actionsParcel.addActions(certifyActions);
if (mUploadKeyCheckbox.isChecked()) { if (mUploadKeyCheckbox.isChecked()) {
actionsParcel.keyServerUri = Preferences.getPreferences(getActivity()) actionsParcel.setParcelableKeyServer(Preferences.getPreferences(getActivity()).getPreferredKeyserver());
.getPreferredKeyserver();
} }
// cached for next cryptoOperation loop // cache for next cryptoOperation loop
cacheActionsParcel(actionsParcel); CertifyActionsParcel certifyActionsParcel = actionsParcel.build();
cacheActionsParcel(certifyActionsParcel);
return actionsParcel; return certifyActionsParcel;
} }
@Override @Override
@@ -67,7 +67,7 @@ public class ConsolidateDialogActivity extends FragmentActivity
@Override @Override
public ConsolidateInputParcel createOperationInput() { public ConsolidateInputParcel createOperationInput() {
return new ConsolidateInputParcel(mRecovery); return ConsolidateInputParcel.createConsolidateInputParcel(mRecovery);
} }
@Override @Override
@@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel; import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
@@ -294,7 +295,7 @@ public class CreateKeyFinalFragment extends Fragment {
} }
private static SaveKeyringParcel createDefaultSaveKeyringParcel(CreateKeyActivity createKeyActivity) { private static SaveKeyringParcel createDefaultSaveKeyringParcel(CreateKeyActivity createKeyActivity) {
SaveKeyringParcel saveKeyringParcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
if (createKeyActivity.mCreateSecurityToken) { if (createKeyActivity.mCreateSecurityToken) {
if (createKeyActivity.mSecurityTokenSign == null) { if (createKeyActivity.mSecurityTokenSign == null) {
@@ -302,37 +303,40 @@ public class CreateKeyFinalFragment extends Fragment {
createKeyActivity.mSecurityTokenDec = Constants.SECURITY_TOKEN_V2_DEC; createKeyActivity.mSecurityTokenDec = Constants.SECURITY_TOKEN_V2_DEC;
createKeyActivity.mSecurityTokenAuth = Constants.SECURITY_TOKEN_V2_AUTH; createKeyActivity.mSecurityTokenAuth = Constants.SECURITY_TOKEN_V2_AUTH;
} }
createKeyActivity.mSecurityTokenSign.addToSaveKeyringParcel(saveKeyringParcel, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER); createKeyActivity.mSecurityTokenSign.addToSaveKeyringParcel(
createKeyActivity.mSecurityTokenDec.addToSaveKeyringParcel(saveKeyringParcel, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE); builder, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER);
createKeyActivity.mSecurityTokenAuth.addToSaveKeyringParcel(saveKeyringParcel, KeyFlags.AUTHENTICATION); createKeyActivity.mSecurityTokenDec.addToSaveKeyringParcel(
builder, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
createKeyActivity.mSecurityTokenAuth.addToSaveKeyringParcel(builder, KeyFlags.AUTHENTICATION);
// use empty passphrase // use empty passphrase
saveKeyringParcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
} else { } else {
Constants.addDefaultSubkeys(saveKeyringParcel); Constants.addDefaultSubkeys(builder);
if (createKeyActivity.mPassphrase != null) { if (createKeyActivity.mPassphrase != null) {
saveKeyringParcel.setNewUnlock(new ChangeUnlockParcel(createKeyActivity.mPassphrase)); builder.setNewUnlock(
ChangeUnlockParcel.createUnLockParcelForNewKey(createKeyActivity.mPassphrase));
} else { } else {
saveKeyringParcel.setNewUnlock(null); builder.setNewUnlock(null);
} }
} }
String userId = KeyRing.createUserId( String userId = KeyRing.createUserId(
new OpenPgpUtils.UserId(createKeyActivity.mName, createKeyActivity.mEmail, null) new OpenPgpUtils.UserId(createKeyActivity.mName, createKeyActivity.mEmail, null)
); );
saveKeyringParcel.mAddUserIds.add(userId); builder.addUserId(userId);
saveKeyringParcel.mChangePrimaryUserId = userId; builder.setChangePrimaryUserId(userId);
if (createKeyActivity.mAdditionalEmails != null if (createKeyActivity.mAdditionalEmails != null
&& createKeyActivity.mAdditionalEmails.size() > 0) { && createKeyActivity.mAdditionalEmails.size() > 0) {
for (String email : createKeyActivity.mAdditionalEmails) { for (String email : createKeyActivity.mAdditionalEmails) {
String thisUserId = KeyRing.createUserId( String thisUserId = KeyRing.createUserId(
new OpenPgpUtils.UserId(createKeyActivity.mName, email, null) new OpenPgpUtils.UserId(createKeyActivity.mName, email, null)
); );
saveKeyringParcel.mAddUserIds.add(thisUserId); builder.addUserId(thisUserId);
} }
} }
return saveKeyringParcel; return builder.build();
} }
private void checkEmailValidity() { private void checkEmailValidity() {
@@ -425,11 +429,11 @@ public class CreateKeyFinalFragment extends Fragment {
private void moveToCard(final EditKeyResult saveKeyResult) { private void moveToCard(final EditKeyResult saveKeyResult) {
CreateKeyActivity activity = (CreateKeyActivity) getActivity(); CreateKeyActivity activity = (CreateKeyActivity) getActivity();
final SaveKeyringParcel changeKeyringParcel; SaveKeyringParcel.Builder builder;
CachedPublicKeyRing key = (KeyRepository.createDatabaseInteractor(getContext())) CachedPublicKeyRing key = (KeyRepository.createDatabaseInteractor(getContext()))
.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId); .getCachedPublicKeyRing(saveKeyResult.mMasterKeyId);
try { try {
changeKeyringParcel = new SaveKeyringParcel(key.getMasterKeyId(), key.getFingerprint()); builder = SaveKeyringParcel.buildChangeKeyringParcel(key.getMasterKeyId(), key.getFingerprint());
} catch (PgpKeyNotFoundException e) { } catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "Key that should be moved to Security Token not found in database!"); Log.e(Constants.TAG, "Key that should be moved to Security Token not found in database!");
return; return;
@@ -437,13 +441,13 @@ public class CreateKeyFinalFragment extends Fragment {
// define subkeys that should be moved to the card // define subkeys that should be moved to the card
Cursor cursor = activity.getContentResolver().query( Cursor cursor = activity.getContentResolver().query(
KeychainContract.Keys.buildKeysUri(changeKeyringParcel.mMasterKeyId), KeychainContract.Keys.buildKeysUri(builder.getMasterKeyId()),
new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null
); );
try { try {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
long subkeyId = cursor.getLong(0); long subkeyId = cursor.getLong(0);
changeKeyringParcel.getOrCreateSubkeyChange(subkeyId).mMoveKeyToSecurityToken = true; builder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(subkeyId));
} }
} finally { } finally {
if (cursor != null) { if (cursor != null) {
@@ -452,15 +456,17 @@ public class CreateKeyFinalFragment extends Fragment {
} }
// define new PIN and Admin PIN for the card // define new PIN and Admin PIN for the card
changeKeyringParcel.mSecurityTokenPin = activity.mSecurityTokenPin; builder.setSecurityTokenPin(activity.mSecurityTokenPin);
changeKeyringParcel.mSecurityTokenAdminPin = activity.mSecurityTokenAdminPin; builder.setSecurityTokenAdminPin(activity.mSecurityTokenAdminPin);
final SaveKeyringParcel saveKeyringParcel = builder.build();
CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> callback CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> callback
= new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() { = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
return changeKeyringParcel; return saveKeyringParcel;
} }
@Override @Override
@@ -499,7 +505,7 @@ public class CreateKeyFinalFragment extends Fragment {
mMoveToCardOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_modify); mMoveToCardOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_modify);
mMoveToCardOpHelper.cryptoOperation(new CryptoInputParcel(new Date())); mMoveToCardOpHelper.cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
} }
private void uploadKey(final EditKeyResult saveKeyResult) { private void uploadKey(final EditKeyResult saveKeyResult) {
@@ -520,7 +526,7 @@ public class CreateKeyFinalFragment extends Fragment {
@Override @Override
public UploadKeyringParcel createOperationInput() { public UploadKeyringParcel createOperationInput() {
return new UploadKeyringParcel(keyserver, masterKeyId); return UploadKeyringParcel.createWithKeyId(keyserver, masterKeyId);
} }
@Override @Override
@@ -235,7 +235,7 @@ public class CreateSecurityTokenImportResetFragment
Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class); Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class);
RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset(); RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset();
intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP); intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP);
intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, new CryptoInputParcel()); intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, CryptoInputParcel.createCryptoInputParcel());
startActivityForResult(intent, REQUEST_CODE_RESET); startActivityForResult(intent, REQUEST_CODE_RESET);
} }
@@ -271,7 +271,7 @@ public class CreateSecurityTokenImportResetFragment
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
@Override @Override
@@ -159,7 +159,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(keyList, keyserver); return ImportKeyringParcel.createImportKeyringParcel(keyList, keyserver);
} }
@Override @Override
@@ -637,9 +637,9 @@ public class DecryptListFragment
return null; return null;
} }
PgpDecryptVerifyInputParcel decryptInput = new PgpDecryptVerifyInputParcel() PgpDecryptVerifyInputParcel.Builder decryptInput = PgpDecryptVerifyInputParcel.builder()
.setAllowSymmetricDecryption(true); .setAllowSymmetricDecryption(true);
return new InputDataParcel(mCurrentInputUri, decryptInput); return InputDataParcel.createInputDataParcel(mCurrentInputUri, decryptInput.build());
} }
@@ -779,7 +779,7 @@ public class DecryptListFragment
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(keyList, keyserver); return ImportKeyringParcel.createImportKeyringParcel(keyList, keyserver);
} }
@Override @Override
@@ -139,7 +139,7 @@ public class DeleteKeyDialogActivity extends FragmentActivity {
} }
private void startRevocationOperation() { private void startRevocationOperation() {
mRevokeOpHelper.cryptoOperation(new CryptoInputParcel(new Date(), false)); mRevokeOpHelper.cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date(), false));
} }
private void startDeletionOperation() { private void startDeletionOperation() {
@@ -151,7 +151,7 @@ public class DeleteKeyDialogActivity extends FragmentActivity {
return new CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult>() { return new CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult>() {
@Override @Override
public RevokeKeyringParcel createOperationInput() { public RevokeKeyringParcel createOperationInput() {
return new RevokeKeyringParcel(mMasterKeyIds[0], true, return RevokeKeyringParcel.createRevokeKeyringParcel(mMasterKeyIds[0], true,
(ParcelableHkpKeyserver) getIntent().getParcelableExtra(EXTRA_KEYSERVER)); (ParcelableHkpKeyserver) getIntent().getParcelableExtra(EXTRA_KEYSERVER));
} }
@@ -183,9 +183,12 @@ public class DeleteKeyDialogActivity extends FragmentActivity {
return new CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult>() { return new CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult>() {
@Override @Override
public DeleteKeyringParcel createOperationInput() { public DeleteKeyringParcel createOperationInput() {
return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret); if (mHasSecret) {
return DeleteKeyringParcel.createDeleteSingleSecretKeyParcel(mMasterKeyIds[0]);
} else {
return DeleteKeyringParcel.createDeletePublicKeysParcel(mMasterKeyIds);
}
} }
@Override @Override
public void onCryptoOperationSuccess(DeleteResult result) { public void onCryptoOperationSuccess(DeleteResult result) {
returnResult(result); returnResult(result);
@@ -82,7 +82,7 @@ public class EditIdentitiesFragment extends Fragment
private Uri mDataUri; private Uri mDataUri;
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditOpHelper; private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditOpHelper;
private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper; private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper;
@@ -180,7 +180,7 @@ public class EditIdentitiesFragment extends Fragment
return; return;
} }
mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, keyRing.getFingerprint());
mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback(); mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback();
} catch (PgpKeyNotFoundException | NotFoundException e) { } catch (PgpKeyNotFoundException | NotFoundException e) {
@@ -193,11 +193,11 @@ public class EditIdentitiesFragment extends Fragment
getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditIdentitiesFragment.this); getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditIdentitiesFragment.this);
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0); mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0);
mUserIdsAdapter.setEditMode(mSaveKeyringParcel); mUserIdsAdapter.setEditMode(mSkpBuilder);
mUserIdsList.setAdapter(mUserIdsAdapter); mUserIdsList.setAdapter(mUserIdsAdapter);
// TODO: SaveParcel from savedInstance?! // TODO: SaveParcel from savedInstance?!
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, false); mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
} }
@@ -266,23 +266,23 @@ public class EditIdentitiesFragment extends Fragment
switch (message.what) { switch (message.what) {
case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID: case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:
// toggle // toggle
if (mSaveKeyringParcel.mChangePrimaryUserId != null if (mSkpBuilder.getChangePrimaryUserId() != null
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { && mSkpBuilder.getChangePrimaryUserId().equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} else { } else {
mSaveKeyringParcel.mChangePrimaryUserId = userId; mSkpBuilder.setChangePrimaryUserId(userId);
} }
break; break;
case EditUserIdDialogFragment.MESSAGE_REVOKE: case EditUserIdDialogFragment.MESSAGE_REVOKE:
// toggle // toggle
if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { if (mSkpBuilder.getMutableRevokeUserIds().contains(userId)) {
mSaveKeyringParcel.mRevokeUserIds.remove(userId); mSkpBuilder.removeRevokeUserId(userId);
} else { } else {
mSaveKeyringParcel.mRevokeUserIds.add(userId); mSkpBuilder.addRevokeUserId(userId);
// not possible to revoke and change to primary user id // not possible to revoke and change to primary user id
if (mSaveKeyringParcel.mChangePrimaryUserId != null if (mSkpBuilder.getChangePrimaryUserId() != null
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { && mSkpBuilder.getChangePrimaryUserId().equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} }
} }
break; break;
@@ -339,7 +339,7 @@ public class EditIdentitiesFragment extends Fragment
= new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() { = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
return mSaveKeyringParcel; return mSkpBuilder.build();
} }
@Override @Override
@@ -395,7 +395,7 @@ public class EditIdentitiesFragment extends Fragment
@Override @Override
public UploadKeyringParcel createOperationInput() { public UploadKeyringParcel createOperationInput() {
return new UploadKeyringParcel(keyserver, masterKeyId); return UploadKeyringParcel.createWithKeyId(keyserver, masterKeyId);
} }
@Override @Override
@@ -100,7 +100,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
private Uri mDataUri; private Uri mDataUri;
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
private String mPrimaryUserId; private String mPrimaryUserId;
@@ -156,7 +156,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
if (mDataUri == null) { if (mDataUri == null) {
returnKeyringParcel(); returnKeyringParcel();
} else { } else {
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
} }
} }
}, new OnClickListener() { }, new OnClickListener() {
@@ -184,13 +184,13 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
} }
private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) { private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) {
mSaveKeyringParcel = saveKeyringParcel; mSkpBuilder = SaveKeyringParcel.buildUpon(saveKeyringParcel);
mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId; mPrimaryUserId = saveKeyringParcel.getChangePrimaryUserId();
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, true); mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), true);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys, true); mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSkpBuilder.getMutableAddSubKeys(), true);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
} }
@@ -213,7 +213,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
return; return;
} }
mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, keyRing.getFingerprint());
mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback(); mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback();
} catch (PgpKeyNotFoundException | NotFoundException e) { } catch (PgpKeyNotFoundException | NotFoundException e) {
@@ -227,18 +227,18 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this); getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this);
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0); mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0);
mUserIdsAdapter.setEditMode(mSaveKeyringParcel); mUserIdsAdapter.setEditMode(mSkpBuilder);
mUserIdsList.setAdapter(mUserIdsAdapter); mUserIdsList.setAdapter(mUserIdsAdapter);
// TODO: SaveParcel from savedInstance?! // TODO: SaveParcel from savedInstance?!
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, false); mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0); mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
mSubkeysAdapter.setEditMode(mSaveKeyringParcel); mSubkeysAdapter.setEditMode(mSkpBuilder);
mSubkeysList.setAdapter(mSubkeysAdapter); mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys, false); mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSkpBuilder.getMutableAddSubKeys(), false);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
} }
@@ -341,7 +341,8 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
Bundle data = message.getData(); Bundle data = message.getData();
// cache new returned passphrase! // cache new returned passphrase!
mSaveKeyringParcel.setNewUnlock(new ChangeUnlockParcel( mSkpBuilder.setNewUnlock(ChangeUnlockParcel.createChangeUnlockParcel(
mSkpBuilder.getMasterKeyId(), mSkpBuilder.getFingerprint(),
(Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE))); (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)));
} }
} }
@@ -367,23 +368,23 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
switch (message.what) { switch (message.what) {
case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID: case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:
// toggle // toggle
if (mSaveKeyringParcel.mChangePrimaryUserId != null String changePrimaryUserId = mSkpBuilder.getChangePrimaryUserId();
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { if (changePrimaryUserId != null && changePrimaryUserId.equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} else { } else {
mSaveKeyringParcel.mChangePrimaryUserId = userId; mSkpBuilder.setChangePrimaryUserId(userId);
} }
break; break;
case EditUserIdDialogFragment.MESSAGE_REVOKE: case EditUserIdDialogFragment.MESSAGE_REVOKE:
// toggle // toggle
if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { if (mSkpBuilder.getMutableRevokeUserIds().contains(userId)) {
mSaveKeyringParcel.mRevokeUserIds.remove(userId); mSkpBuilder.removeRevokeUserId(userId);
} else { } else {
mSaveKeyringParcel.mRevokeUserIds.add(userId); mSkpBuilder.addRevokeUserId(userId);
// not possible to revoke and change to primary user id // not possible to revoke and change to primary user id
if (mSaveKeyringParcel.mChangePrimaryUserId != null if (mSkpBuilder.getChangePrimaryUserId() != null
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { && mSkpBuilder.getChangePrimaryUserId().equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} }
} }
break; break;
@@ -416,10 +417,10 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
break; break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE: case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle // toggle
if (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) { if (mSkpBuilder.getMutableRevokeSubKeys().contains(keyId)) {
mSaveKeyringParcel.mRevokeSubKeys.remove(keyId); mSkpBuilder.removeRevokeSubkey(keyId);
} else { } else {
mSaveKeyringParcel.mRevokeSubKeys.add(keyId); mSkpBuilder.addRevokeSubkey(keyId);
} }
break; break;
case EditSubkeyDialogFragment.MESSAGE_STRIP: { case EditSubkeyDialogFragment.MESSAGE_STRIP: {
@@ -429,16 +430,11 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
break; break;
} }
SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId); SubkeyChange change = mSkpBuilder.getSubkeyChange(keyId);
if (change == null) { if (change == null || !change.getDummyStrip()) {
mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false)); mSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(keyId));
break; } else {
} mSkpBuilder.removeSubkeyChange(change);
// toggle
change.mDummyStrip = !change.mDummyStrip;
if (change.mDummyStrip && change.mMoveKeyToSecurityToken) {
// User had chosen to divert key, but now wants to strip it instead.
change.mMoveKeyToSecurityToken = false;
} }
break; break;
} }
@@ -478,19 +474,13 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
break; break;
} }
SubkeyChange change; SubkeyChange change = mSkpBuilder.getSubkeyChange(keyId);
change = mSaveKeyringParcel.getSubkeyChange(keyId); if (change == null || !change.getMoveKeyToSecurityToken()) {
if (change == null) { mSkpBuilder.addOrReplaceSubkeyChange(
mSaveKeyringParcel.mChangeSubKeys.add( SubkeyChange.createMoveToSecurityTokenChange(keyId));
new SubkeyChange(keyId, false, true)
);
break; break;
} } else {
// toggle mSkpBuilder.removeSubkeyChange(change);
change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
// User had chosen to strip key, but now wants to divert it.
change.mDummyStrip = false;
} }
break; break;
} }
@@ -522,9 +512,10 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
public void handleMessage(Message message) { public void handleMessage(Message message) {
switch (message.what) { switch (message.what) {
case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY: case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY:
mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = Long expiry = (Long) message.getData().getSerializable(
(Long) message.getData().getSerializable(
EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY); EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY);
mSkpBuilder.addOrReplaceSubkeyChange(
SubkeyChange.createFlagsOrExpiryChange(keyId, null, expiry));
break; break;
} }
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
@@ -585,20 +576,20 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
} }
protected void returnKeyringParcel() { protected void returnKeyringParcel() {
if (mSaveKeyringParcel.mAddUserIds.size() == 0) { if (mSkpBuilder.getMutableAddUserIds().size() == 0) {
Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show(); Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show();
return; return;
} }
if (mSaveKeyringParcel.mAddSubKeys.size() == 0) { if (mSkpBuilder.getMutableAddSubKeys().size() == 0) {
Notify.create(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR).show(); Notify.create(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR).show();
return; return;
} }
// use first user id as primary String firstUserId = mSkpBuilder.getMutableAddUserIds().get(0);
mSaveKeyringParcel.mChangePrimaryUserId = mSaveKeyringParcel.mAddUserIds.get(0); mSkpBuilder.setChangePrimaryUserId(firstUserId);
Intent returnIntent = new Intent(); Intent returnIntent = new Intent();
returnIntent.putExtra(EditKeyActivity.EXTRA_SAVE_KEYRING_PARCEL, mSaveKeyringParcel); returnIntent.putExtra(EditKeyActivity.EXTRA_SAVE_KEYRING_PARCEL, mSkpBuilder.build());
getActivity().setResult(Activity.RESULT_OK, returnIntent); getActivity().setResult(Activity.RESULT_OK, returnIntent);
getActivity().finish(); getActivity().finish();
} }
@@ -619,7 +610,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
return mSaveKeyringParcel; return mSkpBuilder.build();
} }
@Override @Override
@@ -345,19 +345,19 @@ public class EncryptFilesFragment
case R.id.encrypt_save: { case R.id.encrypt_save: {
hideKeyboard(); hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.SAVE; mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
case R.id.encrypt_share: { case R.id.encrypt_share: {
hideKeyboard(); hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.SHARE; mAfterEncryptAction = AfterEncryptAction.SHARE;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
case R.id.encrypt_copy: { case R.id.encrypt_copy: {
hideKeyboard(); hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.COPY; mAfterEncryptAction = AfterEncryptAction.COPY;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
case R.id.check_use_armor: { case R.id.check_use_armor: {
@@ -577,12 +577,10 @@ public class EncryptFilesFragment
} }
public SignEncryptParcel createOperationInput() { public SignEncryptParcel createOperationInput() {
SignEncryptParcel actionsParcel = getCachedActionsParcel(); SignEncryptParcel actionsParcel = getCachedActionsParcel();
// we have three cases here: nothing cached, cached except output, fully cached // we have three cases here: nothing cached, cached except output, fully cached
if (actionsParcel == null) { if (actionsParcel == null) {
// clear output uris for now, they will be created by prepareOutputStreams later // clear output uris for now, they will be created by prepareOutputStreams later
mOutputUris = null; mOutputUris = null;
@@ -593,7 +591,6 @@ public class EncryptFilesFragment
} }
cacheActionsParcel(actionsParcel); cacheActionsParcel(actionsParcel);
} }
// if it's incomplete, prepare output streams // if it's incomplete, prepare output streams
@@ -606,9 +603,10 @@ public class EncryptFilesFragment
} }
} }
actionsParcel.addOutputUris(mOutputUris); actionsParcel = SignEncryptParcel.builder(actionsParcel)
.addOutputUris(mOutputUris)
.build();
cacheActionsParcel(actionsParcel); cacheActionsParcel(actionsParcel);
} }
return actionsParcel; return actionsParcel;
@@ -623,21 +621,13 @@ public class EncryptFilesFragment
} }
// fill values for this action // fill values for this action
PgpSignEncryptData data = new PgpSignEncryptData(); PgpSignEncryptData.Builder data = PgpSignEncryptData.builder();
if (mUseCompression) { if (!mUseCompression) {
data.setCompressionAlgorithm( data.setCompressionAlgorithm(PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT);
} else {
data.setCompressionAlgorithm(
PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
} }
data.setHiddenRecipients(mHiddenRecipients); data.setHiddenRecipients(mHiddenRecipients);
data.setEnableAsciiArmorOutput(mAfterEncryptAction == AfterEncryptAction.COPY || mUseArmor); data.setEnableAsciiArmorOutput(mAfterEncryptAction == AfterEncryptAction.COPY || mUseArmor);
data.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
data.setSignatureHashAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
EncryptActivity encryptActivity = (EncryptActivity) getActivity(); EncryptActivity encryptActivity = (EncryptActivity) getActivity();
EncryptModeFragment modeFragment = encryptActivity.getModeFragment(); EncryptModeFragment modeFragment = encryptActivity.getModeFragment();
@@ -675,10 +665,9 @@ public class EncryptFilesFragment
} }
SignEncryptParcel parcel = new SignEncryptParcel(data); SignEncryptParcel.Builder builder = SignEncryptParcel.builder(data.build());
parcel.addInputUris(mFilesAdapter.getAsArrayList()); builder.addInputUris(mFilesAdapter.getAsArrayList());
return builder.build();
return parcel;
} }
private Intent createSendIntent() { private Intent createSendIntent() {
@@ -733,7 +722,7 @@ public class EncryptFilesFragment
mOutputUris.add(data.getData()); mOutputUris.add(data.getData());
// make sure this is correct at this point // make sure this is correct at this point
mAfterEncryptAction = AfterEncryptAction.SAVE; mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
} else if (resultCode == Activity.RESULT_CANCELED) { } else if (resultCode == Activity.RESULT_CANCELED) {
onCryptoOperationCancelled(); onCryptoOperationCancelled();
} }
@@ -17,6 +17,11 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import android.app.Activity; import android.app.Activity;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
@@ -38,7 +43,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -49,10 +54,6 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class EncryptTextFragment public class EncryptTextFragment
extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> { extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> {
@@ -181,18 +182,18 @@ public class EncryptTextFragment
case R.id.encrypt_copy: { case R.id.encrypt_copy: {
hideKeyboard(); hideKeyboard();
mShareAfterEncrypt = false; mShareAfterEncrypt = false;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
case R.id.encrypt_share: { case R.id.encrypt_share: {
hideKeyboard(); hideKeyboard();
mShareAfterEncrypt = true; mShareAfterEncrypt = true;
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
case R.id.encrypt_paste: { case R.id.encrypt_paste: {
hideKeyboard(); hideKeyboard();
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
break; break;
} }
default: { default: {
@@ -233,22 +234,14 @@ public class EncryptTextFragment
} }
// fill values for this action // fill values for this action
PgpSignEncryptData data = new PgpSignEncryptData(); PgpSignEncryptData.Builder data = PgpSignEncryptData.builder();
data.setCleartextSignature(true); data.setCleartextSignature(true);
if (mUseCompression) { if (!mUseCompression) {
data.setCompressionAlgorithm( data.setCompressionAlgorithm(OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT);
} else {
data.setCompressionAlgorithm(
PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
} }
data.setHiddenRecipients(mHiddenRecipients); data.setHiddenRecipients(mHiddenRecipients);
data.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
data.setSignatureHashAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
// Always use armor for messages // Always use armor for messages
data.setEnableAsciiArmorOutput(true); data.setEnableAsciiArmorOutput(true);
@@ -286,10 +279,7 @@ public class EncryptTextFragment
data.setSymmetricPassphrase(passphrase); data.setSymmetricPassphrase(passphrase);
} }
SignEncryptParcel parcel = new SignEncryptParcel(data); return SignEncryptParcel.createSignEncryptParcel(data.build(), mMessage.getBytes());
parcel.setBytes(mMessage.getBytes());
return parcel;
} }
private void copyToClipboard(SignEncryptResult result) { private void copyToClipboard(SignEncryptResult result) {
@@ -357,7 +357,7 @@ public class ImportKeysActivity extends BaseActivity implements ImportKeysListen
return; return;
} }
ImportKeyringParcel inputParcel = new ImportKeyringParcel(null, null); ImportKeyringParcel inputParcel = ImportKeyringParcel.createImportKeyringParcel(null, null);
ImportKeysOperationCallback callback = new ImportKeysOperationCallback(this, inputParcel, null); ImportKeysOperationCallback callback = new ImportKeysOperationCallback(this, inputParcel, null);
mOpHelper = new CryptoOperationHelper<>(1, this, callback, R.string.progress_importing); mOpHelper = new CryptoOperationHelper<>(1, this, callback, R.string.progress_importing);
mOpHelper.cryptoOperation(); mOpHelper.cryptoOperation();
@@ -223,7 +223,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
@Override @Override
@@ -519,7 +519,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
@Override @Override
@@ -553,7 +553,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
@Override @Override
public ConsolidateInputParcel createOperationInput() { public ConsolidateInputParcel createOperationInput() {
return new ConsolidateInputParcel(false); // we want to perform a full consolidate return ConsolidateInputParcel.createConsolidateInputParcel(false); // we want to perform a full consolidate
} }
@Override @Override
@@ -586,7 +586,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
@Override @Override
public BenchmarkInputParcel createOperationInput() { public BenchmarkInputParcel createOperationInput() {
return new BenchmarkInputParcel(); // we want to perform a full consolidate return BenchmarkInputParcel.newInstance(); // we want to perform a full consolidate
} }
@Override @Override
@@ -70,7 +70,7 @@ public class OrbotRequiredDialogActivity extends FragmentActivity
mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT); mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
if (mCryptoInputParcel == null) { if (mCryptoInputParcel == null) {
// compatibility with usages that don't use a CryptoInputParcel // compatibility with usages that don't use a CryptoInputParcel
mCryptoInputParcel = new CryptoInputParcel(); mCryptoInputParcel = CryptoInputParcel.createCryptoInputParcel();
} }
mMessenger = getIntent().getParcelableExtra(EXTRA_MESSENGER); mMessenger = getIntent().getParcelableExtra(EXTRA_MESSENGER);
@@ -147,7 +147,7 @@ public class OrbotRequiredDialogActivity extends FragmentActivity
public void onNeutralButton() { public void onNeutralButton() {
sendMessage(MESSAGE_ORBOT_IGNORE); sendMessage(MESSAGE_ORBOT_IGNORE);
Intent intent = new Intent(); Intent intent = new Intent();
mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy()); mCryptoInputParcel = mCryptoInputParcel.withParcelableProxy(ParcelableProxy.getForNoProxy());
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel); intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
setResult(RESULT_OK, intent); setResult(RESULT_OK, intent);
finish(); finish();
@@ -99,7 +99,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
CryptoInputParcel cryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT); CryptoInputParcel cryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
if (cryptoInputParcel == null) { if (cryptoInputParcel == null) {
cryptoInputParcel = new CryptoInputParcel(); cryptoInputParcel = CryptoInputParcel.createCryptoInputParcel();
getIntent().putExtra(EXTRA_CRYPTO_INPUT, cryptoInputParcel); getIntent().putExtra(EXTRA_CRYPTO_INPUT, cryptoInputParcel);
} }
@@ -117,7 +117,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
if (pubRing.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) { if (pubRing.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) {
// also return passphrase back to activity // also return passphrase back to activity
Intent returnIntent = new Intent(); Intent returnIntent = new Intent();
cryptoInputParcel.mPassphrase = new Passphrase(""); cryptoInputParcel = cryptoInputParcel.withPassphrase(new Passphrase(""));
returnIntent.putExtra(RESULT_CRYPTO_INPUT, cryptoInputParcel); returnIntent.putExtra(RESULT_CRYPTO_INPUT, cryptoInputParcel);
setResult(RESULT_OK, returnIntent); setResult(RESULT_OK, returnIntent);
finish(); finish();
@@ -539,7 +539,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
CryptoInputParcel inputParcel = getArguments().getParcelable(EXTRA_CRYPTO_INPUT); CryptoInputParcel inputParcel = getArguments().getParcelable(EXTRA_CRYPTO_INPUT);
// noinspection ConstantConditions, we handle the non-null case in PassphraseDialogActivity.onCreate() // noinspection ConstantConditions, we handle the non-null case in PassphraseDialogActivity.onCreate()
inputParcel.mPassphrase = passphrase; inputParcel = inputParcel.withPassphrase(passphrase);
((PassphraseDialogActivity) getActivity()).handleResult(inputParcel); ((PassphraseDialogActivity) getActivity()).handleResult(inputParcel);
@@ -185,7 +185,7 @@ public class SafeSlingerActivity extends BaseActivity
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
@Override @Override
@@ -206,7 +206,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
for (int i = 0; i < mRequiredInput.mInputData.length; i++) { for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] encryptedSessionKey = mRequiredInput.mInputData[i]; byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey, publicKeyRing.getPublicKey(tokenKeyId)); byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey, publicKeyRing.getPublicKey(tokenKeyId));
mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey); mInputParcel = mInputParcel.withCryptoData(encryptedSessionKey, decryptedSessionKey);
} }
break; break;
} }
@@ -218,13 +218,13 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
throw new IOException(getString(R.string.error_wrong_security_token)); throw new IOException(getString(R.string.error_wrong_security_token));
} }
mInputParcel.addSignatureTime(mRequiredInput.mSignatureTime); mInputParcel = mInputParcel.withSignatureTime(mRequiredInput.mSignatureTime);
for (int i = 0; i < mRequiredInput.mInputData.length; i++) { for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i]; byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i]; int algo = mRequiredInput.mSignAlgos[i];
byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo); byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo);
mInputParcel.addCryptoData(hash, signedHash); mInputParcel = mInputParcel.withCryptoData(hash, signedHash);
} }
break; break;
} }
@@ -266,7 +266,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
mSecurityTokenHelper.changeKey(key, passphrase); mSecurityTokenHelper.changeKey(key, passphrase);
// TODO: Is this really used anywhere? // TODO: Is this really used anywhere?
mInputParcel.addCryptoData(subkeyBytes, tokenSerialNumber); mInputParcel = mInputParcel.withCryptoData(subkeyBytes, tokenSerialNumber);
} }
// change PINs afterwards // change PINs afterwards
@@ -144,7 +144,7 @@ public class UploadKeyActivity extends BaseActivity
public UploadKeyringParcel createOperationInput() { public UploadKeyringParcel createOperationInput() {
long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS); long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS);
return new UploadKeyringParcel(mKeyserver, masterKeyIds[0]); return UploadKeyringParcel.createWithKeyId(mKeyserver, masterKeyIds[0]);
} }
@Override @Override
@@ -82,7 +82,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
private long mMasterKeyId; private long mMasterKeyId;
private byte[] mFingerprint; private byte[] mFingerprint;
private boolean mHasSecret; private boolean mHasSecret;
private SaveKeyringParcel mEditModeSaveKeyringParcel; private SaveKeyringParcel.Builder mEditModeSkpBuilder;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
@@ -250,15 +250,15 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mEditModeSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint); mEditModeSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint);
mSubkeysAddedAdapter = mSubkeysAddedAdapter = new SubkeysAddedAdapter(
new SubkeysAddedAdapter(getActivity(), mEditModeSaveKeyringParcel.mAddSubKeys, false); getActivity(), mEditModeSkpBuilder.getMutableAddSubKeys(), false);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
mSubkeysAddedLayout.setVisibility(View.VISIBLE); mSubkeysAddedLayout.setVisibility(View.VISIBLE);
mSubkeyAddFabLayout.setDisplayedChild(1); mSubkeyAddFabLayout.setDisplayedChild(1);
mSubkeysAdapter.setEditMode(mEditModeSaveKeyringParcel); mSubkeysAdapter.setEditMode(mEditModeSkpBuilder);
getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this); getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this);
mode.setTitle(R.string.title_edit_subkeys); mode.setTitle(R.string.title_edit_subkeys);
@@ -280,7 +280,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
@Override @Override
public void onDestroyActionMode(ActionMode mode) { public void onDestroyActionMode(ActionMode mode) {
mEditModeSaveKeyringParcel = null; mEditModeSkpBuilder = null;
mSubkeysAdapter.setEditMode(null); mSubkeysAdapter.setEditMode(null);
mSubkeysAddedLayout.setVisibility(View.GONE); mSubkeysAddedLayout.setVisibility(View.GONE);
mSubkeyAddFabLayout.setDisplayedChild(0); mSubkeyAddFabLayout.setDisplayedChild(0);
@@ -323,10 +323,10 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
break; break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE: case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle // toggle
if (mEditModeSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) { if (mEditModeSkpBuilder.getMutableRevokeSubKeys().contains(keyId)) {
mEditModeSaveKeyringParcel.mRevokeSubKeys.remove(keyId); mEditModeSkpBuilder.removeRevokeSubkey(keyId);
} else { } else {
mEditModeSaveKeyringParcel.mRevokeSubKeys.add(keyId); mEditModeSkpBuilder.addRevokeSubkey(keyId);
} }
break; break;
case EditSubkeyDialogFragment.MESSAGE_STRIP: { case EditSubkeyDialogFragment.MESSAGE_STRIP: {
@@ -336,16 +336,11 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
break; break;
} }
SubkeyChange change = mEditModeSaveKeyringParcel.getSubkeyChange(keyId); SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(keyId);
if (change == null) { if (change == null || !change.getDummyStrip()) {
mEditModeSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false)); mEditModeSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(keyId));
break; } else {
} mEditModeSkpBuilder.removeSubkeyChange(change);
// toggle
change.mDummyStrip = !change.mDummyStrip;
if (change.mDummyStrip && change.mMoveKeyToSecurityToken) {
// User had chosen to divert key, but now wants to strip it instead.
change.mMoveKeyToSecurityToken = false;
} }
break; break;
} }
@@ -385,19 +380,12 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
break; break;
} }
SubkeyChange change; SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(keyId);
change = mEditModeSaveKeyringParcel.getSubkeyChange(keyId); if (change == null || !change.getMoveKeyToSecurityToken()) {
if (change == null) { mEditModeSkpBuilder.addOrReplaceSubkeyChange(
mEditModeSaveKeyringParcel.mChangeSubKeys.add( SubkeyChange.createMoveToSecurityTokenChange(keyId));
new SubkeyChange(keyId, false, true) } else {
); mEditModeSkpBuilder.removeSubkeyChange(change);
break;
}
// toggle
change.mMoveKeyToSecurityToken = !change.mMoveKeyToSecurityToken;
if (change.mMoveKeyToSecurityToken && change.mDummyStrip) {
// User had chosen to strip key, but now wants to divert it.
change.mDummyStrip = false;
} }
break; break;
} }
@@ -429,9 +417,10 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
public void handleMessage(Message message) { public void handleMessage(Message message) {
switch (message.what) { switch (message.what) {
case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY: case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY:
mEditModeSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = Long expiry = (Long) message.getData().getSerializable(
(Long) message.getData().getSerializable(
EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY); EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY);
mEditModeSkpBuilder.addOrReplaceSubkeyChange(
SubkeyChange.createFlagsOrExpiryChange(keyId, null, expiry));
break; break;
} }
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
@@ -458,7 +447,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
return mEditModeSaveKeyringParcel; return mEditModeSkpBuilder.build();
} }
@Override @Override
@@ -79,7 +79,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
private long mMasterKeyId; private long mMasterKeyId;
private byte[] mFingerprint; private byte[] mFingerprint;
private boolean mHasSecret; private boolean mHasSecret;
private SaveKeyringParcel mEditModeSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
@@ -122,7 +122,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
} }
private void showOrEditUserIdInfo(final int position) { private void showOrEditUserIdInfo(final int position) {
if (mEditModeSaveKeyringParcel != null) { if (mSkpBuilder != null) {
editUserId(position); editUserId(position);
} else { } else {
showUserIdInfo(position); showUserIdInfo(position);
@@ -140,23 +140,23 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
switch (message.what) { switch (message.what) {
case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID: case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:
// toggle // toggle
if (mEditModeSaveKeyringParcel.mChangePrimaryUserId != null if (mSkpBuilder.getChangePrimaryUserId() != null
&& mEditModeSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { && mSkpBuilder.getChangePrimaryUserId().equals(userId)) {
mEditModeSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} else { } else {
mEditModeSaveKeyringParcel.mChangePrimaryUserId = userId; mSkpBuilder.setChangePrimaryUserId(userId);
} }
break; break;
case EditUserIdDialogFragment.MESSAGE_REVOKE: case EditUserIdDialogFragment.MESSAGE_REVOKE:
// toggle // toggle
if (mEditModeSaveKeyringParcel.mRevokeUserIds.contains(userId)) { if (mSkpBuilder.getMutableRevokeUserIds().contains(userId)) {
mEditModeSaveKeyringParcel.mRevokeUserIds.remove(userId); mSkpBuilder.removeRevokeUserId(userId);
} else { } else {
mEditModeSaveKeyringParcel.mRevokeUserIds.add(userId); mSkpBuilder.addRevokeUserId(userId);
// not possible to revoke and change to primary user id // not possible to revoke and change to primary user id
if (mEditModeSaveKeyringParcel.mChangePrimaryUserId != null if (mSkpBuilder.getChangePrimaryUserId() != null
&& mEditModeSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { && mSkpBuilder.getChangePrimaryUserId().equals(userId)) {
mEditModeSaveKeyringParcel.mChangePrimaryUserId = null; mSkpBuilder.setChangePrimaryUserId(null);
} }
} }
break; break;
@@ -342,15 +342,15 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mEditModeSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint); mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint);
mUserIdsAddedAdapter = mUserIdsAddedAdapter =
new UserIdsAddedAdapter(getActivity(), mEditModeSaveKeyringParcel.mAddUserIds, false); new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mUserIdsAddedLayout.setVisibility(View.VISIBLE); mUserIdsAddedLayout.setVisibility(View.VISIBLE);
mUserIdAddFabLayout.setDisplayedChild(1); mUserIdAddFabLayout.setDisplayedChild(1);
mUserIdsAdapter.setEditMode(mEditModeSaveKeyringParcel); mUserIdsAdapter.setEditMode(mSkpBuilder);
getLoaderManager().restartLoader(LOADER_ID_USER_IDS, null, ViewKeyAdvUserIdsFragment.this); getLoaderManager().restartLoader(LOADER_ID_USER_IDS, null, ViewKeyAdvUserIdsFragment.this);
mode.setTitle(R.string.title_edit_identities); mode.setTitle(R.string.title_edit_identities);
@@ -372,7 +372,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
@Override @Override
public void onDestroyActionMode(ActionMode mode) { public void onDestroyActionMode(ActionMode mode) {
mEditModeSaveKeyringParcel = null; mSkpBuilder = null;
mUserIdsAdapter.setEditMode(null); mUserIdsAdapter.setEditMode(null);
mUserIdsAddedLayout.setVisibility(View.GONE); mUserIdsAddedLayout.setVisibility(View.GONE);
mUserIdAddFabLayout.setDisplayedChild(0); mUserIdAddFabLayout.setDisplayedChild(0);
@@ -387,7 +387,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
return mEditModeSaveKeyringParcel; return mSkpBuilder.build();
} }
@Override @Override
@@ -213,7 +213,7 @@ public class ViewKeySecurityTokenFragment
@Override @Override
public PromoteKeyringParcel createOperationInput() { public PromoteKeyringParcel createOperationInput() {
return new PromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds); return PromoteKeyringParcel.createPromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
} }
@Override @Override
@@ -237,7 +237,11 @@ public class ImportKeysAdapter extends RecyclerView.Adapter<ImportKeysAdapter.Vi
keyserver = entry.getKeyserver(); keyserver = entry.getKeyserver();
} }
return new ImportKeyringParcel(keysList, keyserver, skipSave); if (skipSave) {
return ImportKeyringParcel.createWithSkipSave(keysList, keyserver);
} else {
return ImportKeyringParcel.createImportKeyringParcel(keysList, keyserver);
}
} }
@Override @Override
@@ -17,6 +17,9 @@
package org.sufficientlysecure.keychain.ui.adapter; package org.sufficientlysecure.keychain.ui.adapter;
import java.util.ArrayList;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.os.Parcel; import android.os.Parcel;
@@ -35,8 +38,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import java.util.ArrayList;
public class MultiUserIdsAdapter extends CursorAdapter { public class MultiUserIdsAdapter extends CursorAdapter {
private LayoutInflater mInflater; private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates; private final ArrayList<Boolean> mCheckStates;
@@ -178,11 +179,12 @@ public class MultiUserIdsAdapter extends CursorAdapter {
p.recycle(); p.recycle();
CertifyAction action = actions.get(keyId); CertifyAction action = actions.get(keyId);
if (actions.get(keyId) == null) { if (action == null) {
actions.put(keyId, new CertifyAction(keyId, uids, null)); action = CertifyAction.createForUserIds(keyId, uids);
} else { } else {
action.mUserIds.addAll(uids); action = action.withAddedUserIds(uids);
} }
actions.put(keyId, action);
} }
} }
@@ -17,6 +17,11 @@
package org.sufficientlysecure.keychain.ui.adapter; package org.sufficientlysecure.keychain.ui.adapter;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.database.Cursor; import android.database.Cursor;
@@ -42,13 +47,9 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class SubkeysAdapter extends CursorAdapter { public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater; private LayoutInflater mInflater;
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
private boolean mHasAnySecret; private boolean mHasAnySecret;
private ColorStateList mDefaultTextColor; private ColorStateList mDefaultTextColor;
@@ -177,12 +178,9 @@ public class SubkeysAdapter extends CursorAdapter {
cursor.getString(INDEX_KEY_CURVE_OID) cursor.getString(INDEX_KEY_CURVE_OID)
)); ));
SubkeyChange change = mSaveKeyringParcel != null SubkeyChange change = mSkpBuilder != null ? mSkpBuilder.getSubkeyChange(keyId) : null;
? mSaveKeyringParcel.getSubkeyChange(keyId) if (change != null && (change.getDummyStrip() || change.getMoveKeyToSecurityToken())) {
: null; if (change.getDummyStrip()) {
if (change != null && (change.mDummyStrip || change.mMoveKeyToSecurityToken)) {
if (change.mDummyStrip) {
algorithmStr.append(", "); algorithmStr.append(", ");
final SpannableString boldStripped = new SpannableString( final SpannableString boldStripped = new SpannableString(
context.getString(R.string.key_stripped) context.getString(R.string.key_stripped)
@@ -190,7 +188,7 @@ public class SubkeysAdapter extends CursorAdapter {
boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
algorithmStr.append(boldStripped); algorithmStr.append(boldStripped);
} }
if (change.mMoveKeyToSecurityToken) { if (change.getMoveKeyToSecurityToken()) {
algorithmStr.append(", "); algorithmStr.append(", ");
final SpannableString boldDivert = new SpannableString( final SpannableString boldDivert = new SpannableString(
context.getString(R.string.key_divert) context.getString(R.string.key_divert)
@@ -242,8 +240,8 @@ public class SubkeysAdapter extends CursorAdapter {
} }
// for edit key // for edit key
if (mSaveKeyringParcel != null) { if (mSkpBuilder != null) {
boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)); boolean revokeThisSubkey = (mSkpBuilder.getMutableRevokeSubKeys().contains(keyId));
if (revokeThisSubkey) { if (revokeThisSubkey) {
if (!isRevoked) { if (!isRevoked) {
@@ -251,12 +249,12 @@ public class SubkeysAdapter extends CursorAdapter {
} }
} }
SaveKeyringParcel.SubkeyChange subkeyChange = mSaveKeyringParcel.getSubkeyChange(keyId); SaveKeyringParcel.SubkeyChange subkeyChange = mSkpBuilder.getSubkeyChange(keyId);
if (subkeyChange != null) { if (subkeyChange != null) {
if (subkeyChange.mExpiry == null || subkeyChange.mExpiry == 0L) { if (subkeyChange.getExpiry() == null || subkeyChange.getExpiry() == 0L) {
expiryDate = null; expiryDate = null;
} else { } else {
expiryDate = new Date(subkeyChange.mExpiry * 1000); expiryDate = new Date(subkeyChange.getExpiry() * 1000);
} }
} }
@@ -345,7 +343,7 @@ public class SubkeysAdapter extends CursorAdapter {
// Disable selection of items, http://stackoverflow.com/a/4075045 // Disable selection of items, http://stackoverflow.com/a/4075045
@Override @Override
public boolean areAllItemsEnabled() { public boolean areAllItemsEnabled() {
if (mSaveKeyringParcel == null) { if (mSkpBuilder == null) {
return false; return false;
} else { } else {
return super.areAllItemsEnabled(); return super.areAllItemsEnabled();
@@ -355,7 +353,7 @@ public class SubkeysAdapter extends CursorAdapter {
// Disable selection of items, http://stackoverflow.com/a/4075045 // Disable selection of items, http://stackoverflow.com/a/4075045
@Override @Override
public boolean isEnabled(int position) { public boolean isEnabled(int position) {
if (mSaveKeyringParcel == null) { if (mSkpBuilder == null) {
return false; return false;
} else { } else {
return super.isEnabled(position); return super.isEnabled(position);
@@ -370,10 +368,10 @@ public class SubkeysAdapter extends CursorAdapter {
* *
* @see SaveKeyringParcel * @see SaveKeyringParcel
* *
* @param saveKeyringParcel The parcel to get info from, or null to leave edit mode. * @param builder The parcel to get info from, or null to leave edit mode.
*/ */
public void setEditMode(@Nullable SaveKeyringParcel saveKeyringParcel) { public void setEditMode(@Nullable SaveKeyringParcel.Builder builder) {
mSaveKeyringParcel = saveKeyringParcel; mSkpBuilder = builder;
} }
} }
@@ -100,9 +100,9 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
String algorithmStr = KeyFormattingUtils.getAlgorithmInfo( String algorithmStr = KeyFormattingUtils.getAlgorithmInfo(
mActivity, mActivity,
holder.mModel.mAlgorithm, holder.mModel.getAlgorithm(),
holder.mModel.mKeySize, holder.mModel.getKeySize(),
holder.mModel.mCurve holder.mModel.getCurve()
); );
boolean isMasterKey = mNewKeyring && position == 0; boolean isMasterKey = mNewKeyring && position == 0;
@@ -148,8 +148,8 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
holder.vKeyId.setText(R.string.edit_key_new_subkey); holder.vKeyId.setText(R.string.edit_key_new_subkey);
holder.vKeyDetails.setText(algorithmStr); holder.vKeyDetails.setText(algorithmStr);
if (holder.mModel.mExpiry != 0L) { if (holder.mModel.getExpiry() != 0L) {
Date expiryDate = new Date(holder.mModel.mExpiry * 1000); Date expiryDate = new Date(holder.mModel.getExpiry() * 1000);
Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expiryCal.setTime(expiryDate); expiryCal.setTime(expiryDate);
// convert from UTC to time zone of device // convert from UTC to time zone of device
@@ -162,7 +162,7 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
+ getContext().getString(R.string.none)); + getContext().getString(R.string.none));
} }
int flags = holder.mModel.mFlags; int flags = holder.mModel.getFlags();
if ((flags & KeyFlags.CERTIFY_OTHER) > 0) { if ((flags & KeyFlags.CERTIFY_OTHER) > 0) {
holder.vCertifyIcon.setVisibility(View.VISIBLE); holder.vCertifyIcon.setVisibility(View.VISIBLE);
} else { } else {
@@ -32,9 +32,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
@@ -43,20 +41,18 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
public class UserIdsAdapter extends UserAttributesAdapter { public class UserIdsAdapter extends UserAttributesAdapter {
protected LayoutInflater mInflater; protected LayoutInflater mInflater;
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
private boolean mShowStatusImages; private boolean mShowStatusImages;
public UserIdsAdapter(Context context, Cursor c, int flags, public UserIdsAdapter(Context context, Cursor c, int flags, boolean showStatusImages) {
boolean showStatusImages, SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags); super(context, c, flags);
mInflater = LayoutInflater.from(context); mInflater = LayoutInflater.from(context);
mSaveKeyringParcel = saveKeyringParcel;
mShowStatusImages = showStatusImages; mShowStatusImages = showStatusImages;
} }
public UserIdsAdapter(Context context, Cursor c, int flags) { public UserIdsAdapter(Context context, Cursor c, int flags) {
this(context, c, flags, true, null); this(context, c, flags, true);
} }
@Override @Override
@@ -96,11 +92,11 @@ public class UserIdsAdapter extends UserAttributesAdapter {
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
// for edit key // for edit key
if (mSaveKeyringParcel != null) { if (mSkpBuilder != null) {
boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); String changePrimaryUserId = mSkpBuilder.getChangePrimaryUserId();
boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null boolean changeAnyPrimaryUserId = (changePrimaryUserId != null);
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)); boolean changeThisPrimaryUserId = (changeAnyPrimaryUserId && changePrimaryUserId.equals(userId));
boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId)); boolean revokeThisUserId = (mSkpBuilder.getMutableRevokeUserIds().contains(userId));
// only if primary user id will be changed // only if primary user id will be changed
// (this is not triggered if the user id is currently the primary one) // (this is not triggered if the user id is currently the primary one)
@@ -161,8 +157,8 @@ public class UserIdsAdapter extends UserAttributesAdapter {
String userId = mCursor.getString(INDEX_USER_ID); String userId = mCursor.getString(INDEX_USER_ID);
boolean isRevokedPending = false; boolean isRevokedPending = false;
if (mSaveKeyringParcel != null) { if (mSkpBuilder != null) {
if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { if (mSkpBuilder.getMutableRevokeUserIds().contains(userId)) {
isRevokedPending = true; isRevokedPending = true;
} }
@@ -181,8 +177,8 @@ public class UserIdsAdapter extends UserAttributesAdapter {
* *
* @param saveKeyringParcel The parcel to get info from, or null to leave edit mode. * @param saveKeyringParcel The parcel to get info from, or null to leave edit mode.
*/ */
public void setEditMode(@Nullable SaveKeyringParcel saveKeyringParcel) { public void setEditMode(@Nullable SaveKeyringParcel.Builder saveKeyringParcel) {
mSaveKeyringParcel = saveKeyringParcel; mSkpBuilder = saveKeyringParcel;
} }
@Override @Override
@@ -323,7 +323,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
} }
public void cryptoOperation() { public void cryptoOperation() {
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
} }
public void onHandleResult(OperationResult result) { public void onHandleResult(OperationResult result) {
@@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Choice; import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList; import java.util.ArrayList;
@@ -304,7 +305,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
expiry = selectedCal.getTime().getTime() / 1000; expiry = selectedCal.getTime().getTime() / 1000;
} }
SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd( SaveKeyringParcel.SubkeyAdd newSubkey = SubkeyAdd.createSubkeyAdd(
algorithm, keySize, curve, flags, expiry algorithm, keySize, curve, flags, expiry
); );
mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey); mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey);
@@ -542,14 +542,14 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
@Nullable @Nullable
@Override @Override
public Parcelable createOperationInput() { public Parcelable createOperationInput() {
CertifyAction action = new CertifyAction(mMasterKeyId, null, CertifyAction action = CertifyAction.createForUserAttributes(mMasterKeyId,
Collections.singletonList(mLinkedId.toUserAttribute())); Collections.singletonList(mLinkedId.toUserAttribute()));
// fill values for this action // fill values for this action
CertifyActionsParcel parcel = new CertifyActionsParcel(mCertifyKeyId); CertifyActionsParcel.Builder builder = CertifyActionsParcel.builder(mCertifyKeyId);
parcel.mCertifyActions.addAll(Collections.singletonList(action)); builder.addActions(Collections.singletonList(action));
return parcel; return builder.build();
} }
@Override @Override
@@ -474,9 +474,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
Bundle data = message.getData(); Bundle data = message.getData();
// use new passphrase! // use new passphrase!
mChangeUnlockParcel = new ChangeUnlockParcel( mChangeUnlockParcel = ChangeUnlockParcel.createChangeUnlockParcel(
mMasterKeyId, mMasterKeyId, mFingerprint,
mFingerprint,
(Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE) (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
); );
@@ -1176,7 +1175,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
@Override @Override
@@ -143,7 +143,7 @@ public class ViewKeyFragment extends LoaderFragment implements LoaderManager.Loa
mIsSecret = getArguments().getBoolean(ARG_IS_SECRET); mIsSecret = getArguments().getBoolean(ARG_IS_SECRET);
// load user ids after we know if it's a secret key // load user ids after we know if it's a secret key
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret, null); mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret);
mUserIds.setAdapter(mUserIdsAdapter); mUserIds.setAdapter(mUserIdsAdapter);
// initialize loaders, which will take care of auto-refresh on change // initialize loaders, which will take care of auto-refresh on change
@@ -210,15 +210,11 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen
@Nullable @Nullable
@Override @Override
public Parcelable createOperationInput() { public Parcelable createOperationInput() {
SaveKeyringParcel skp = SaveKeyringParcel.Builder builder=
new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); SaveKeyringParcel.buildChangeKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint);
WrappedUserAttribute ua = LinkedAttribute.fromResource(mVerifiedResource).toUserAttribute();
WrappedUserAttribute ua = builder.addUserAttribute(ua);
LinkedAttribute.fromResource(mVerifiedResource).toUserAttribute(); return builder.build();
skp.mAddUserAttribute.add(ua);
return skp;
} }
@Override @Override
@@ -96,7 +96,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment<SaveKe
byte[] mFingerprint; byte[] mFingerprint;
long mMasterKeyId; long mMasterKeyId;
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel.Builder mSkpBuilder;
private TextView mLinkedIdTitle, mLinkedIdComment; private TextView mLinkedIdTitle, mLinkedIdComment;
private boolean mFinishOnStop; private boolean mFinishOnStop;
@@ -405,13 +405,10 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment<SaveKe
new Handler().postDelayed(new Runnable() { new Handler().postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
WrappedUserAttribute ua = LinkedAttribute.fromResource(resource).toUserAttribute(); WrappedUserAttribute ua = LinkedAttribute.fromResource(resource).toUserAttribute();
mSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint); mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint);
mSaveKeyringParcel.mAddUserAttribute.add(ua); mSkpBuilder.addUserAttribute(ua);
cryptoOperation(); cryptoOperation();
} }
}, 250); }, 250);
@@ -421,7 +418,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment<SaveKe
@Override @Override
public SaveKeyringParcel createOperationInput() { public SaveKeyringParcel createOperationInput() {
// if this is null, the cryptoOperation silently aborts - which is what we want in that case // if this is null, the cryptoOperation silently aborts - which is what we want in that case
return mSaveKeyringParcel; return mSkpBuilder.build();
} }
@Override @Override
@@ -0,0 +1,35 @@
package org.sufficientlysecure.keychain.util;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.os.Parcel;
import com.ryanharter.auto.value.parcel.TypeAdapter;
public class ByteMapParcelAdapter implements TypeAdapter<Map<ByteBuffer,byte[]>> {
@Override
public Map<ByteBuffer, byte[]> fromParcel(Parcel source) {
int count = source.readInt();
Map<ByteBuffer,byte[]> result = new HashMap<>(count);
for (int i = 0; i < count; i++) {
byte[] key = source.createByteArray();
byte[] value = source.createByteArray();
result.put(ByteBuffer.wrap(key), value);
}
return Collections.unmodifiableMap(result);
}
@Override
public void toParcel(Map<ByteBuffer, byte[]> value, Parcel dest) {
dest.writeInt(value.size());
for (Map.Entry<ByteBuffer, byte[]> entry : value.entrySet()) {
dest.writeByteArray(entry.getKey().array());
dest.writeByteArray(entry.getValue());
}
}
}
@@ -65,7 +65,7 @@ public class EmailKeyHelper {
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
} }
} }
@@ -25,7 +25,7 @@ import android.widget.EditText;
import org.bouncycastle.bcpg.S2K; import org.bouncycastle.bcpg.S2K;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.ComparableS2K; import org.sufficientlysecure.keychain.pgp.ParcelableS2K;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -49,7 +49,7 @@ import java.util.Map.Entry;
*/ */
public class Passphrase implements Parcelable { public class Passphrase implements Parcelable {
private char[] mPassphrase; private char[] mPassphrase;
private HashMap<ComparableS2K, byte[]> mCachedSessionKeys; private HashMap<ParcelableS2K, byte[]> mCachedSessionKeys;
/** /**
* According to http://stackoverflow.com/a/15844273 EditText is not using String internally * According to http://stackoverflow.com/a/15844273 EditText is not using String internally
@@ -104,7 +104,7 @@ public class Passphrase implements Parcelable {
if (mCachedSessionKeys == null) { if (mCachedSessionKeys == null) {
return null; return null;
} }
return mCachedSessionKeys.get(new ComparableS2K(keyEncryptionAlgorithm, s2k)); return mCachedSessionKeys.get(ParcelableS2K.fromS2K(keyEncryptionAlgorithm, s2k));
} }
/** Adds a session key for a set of s2k parameters to this Passphrase object's /** Adds a session key for a set of s2k parameters to this Passphrase object's
@@ -116,7 +116,7 @@ public class Passphrase implements Parcelable {
if (mCachedSessionKeys == null) { if (mCachedSessionKeys == null) {
mCachedSessionKeys = new HashMap<>(); mCachedSessionKeys = new HashMap<>();
} }
mCachedSessionKeys.put(new ComparableS2K(keyEncryptionAlgorithm, s2k), sessionKey); mCachedSessionKeys.put(ParcelableS2K.fromS2K(keyEncryptionAlgorithm, s2k), sessionKey);
} }
/** /**
@@ -184,7 +184,7 @@ public class Passphrase implements Parcelable {
} }
mCachedSessionKeys = new HashMap<>(size); mCachedSessionKeys = new HashMap<>(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
ComparableS2K cachedS2K = source.readParcelable(getClass().getClassLoader()); ParcelableS2K cachedS2K = source.readParcelable(getClass().getClassLoader());
byte[] cachedSessionKey = source.createByteArray(); byte[] cachedSessionKey = source.createByteArray();
mCachedSessionKeys.put(cachedS2K, cachedSessionKey); mCachedSessionKeys.put(cachedS2K, cachedSessionKey);
} }
@@ -197,7 +197,7 @@ public class Passphrase implements Parcelable {
return; return;
} }
dest.writeInt(mCachedSessionKeys.size()); dest.writeInt(mCachedSessionKeys.size());
for (Entry<ComparableS2K,byte[]> entry : mCachedSessionKeys.entrySet()) { for (Entry<ParcelableS2K,byte[]> entry : mCachedSessionKeys.entrySet()) {
dest.writeParcelable(entry.getKey(), 0); dest.writeParcelable(entry.getKey(), 0);
dest.writeByteArray(entry.getValue()); dest.writeByteArray(entry.getValue());
} }
@@ -58,6 +58,7 @@ import org.sufficientlysecure.keychain.service.BackupKeyringParcel;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
@@ -94,17 +95,17 @@ public class BackupOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snips"); builder.addUserId("snips");
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
assertTrue("initial test key creation must succeed", result.success()); assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -112,17 +113,17 @@ public class BackupOperationTest {
} }
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snails"); builder.addUserId("snails");
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase("1234"))); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("1234")));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
assertTrue("initial test key creation must succeed", result.success()); assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -251,7 +252,7 @@ public class BackupOperationTest {
BackupOperation op = new BackupOperation(spyApplication, BackupOperation op = new BackupOperation(spyApplication,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
BackupKeyringParcel parcel = new BackupKeyringParcel( BackupKeyringParcel parcel = BackupKeyringParcel.createBackupKeyringParcel(
new long[] { mStaticRing1.getMasterKeyId() }, false, false, true, fakeOutputUri); new long[] { mStaticRing1.getMasterKeyId() }, false, false, true, fakeOutputUri);
ExportResult result = op.execute(parcel, null); ExportResult result = op.execute(parcel, null);
@@ -308,9 +309,9 @@ public class BackupOperationTest {
BackupOperation op = new BackupOperation(spyApplication, BackupOperation op = new BackupOperation(spyApplication,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
BackupKeyringParcel parcel = new BackupKeyringParcel( BackupKeyringParcel parcel = BackupKeyringParcel.createBackupKeyringParcel(
new long[] { mStaticRing1.getMasterKeyId() }, false, true, true, fakeOutputUri); new long[] { mStaticRing1.getMasterKeyId() }, false, true, true, fakeOutputUri);
CryptoInputParcel inputParcel = new CryptoInputParcel(passphrase); CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel(passphrase);
ExportResult result = op.execute(parcel, inputParcel); ExportResult result = op.execute(parcel, inputParcel);
verify(mockResolver).openOutputStream(fakePipedUri); verify(mockResolver).openOutputStream(fakePipedUri);
@@ -326,23 +327,26 @@ public class BackupOperationTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(outStream.toByteArray()); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowSymmetricDecryption(true); .setAllowSymmetricDecryption(true)
.setInputBytes(outStream.toByteArray())
.build();
{ {
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel()); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel());
assertTrue("decryption must return pending without passphrase", result.isPending()); assertTrue("decryption must return pending without passphrase", result.isPending());
Assert.assertTrue("should contain pending passphrase log entry", Assert.assertTrue("should contain pending passphrase log entry",
result.getLog().containsType(LogType.MSG_DC_PENDING_PASSPHRASE)); result.getLog().containsType(LogType.MSG_DC_PENDING_PASSPHRASE));
} }
{ {
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(new Passphrase("bad"))); DecryptVerifyResult result = op.execute(input,
CryptoInputParcel.createCryptoInputParcel(new Passphrase("bad")));
assertFalse("decryption must fail with bad passphrase", result.success()); assertFalse("decryption must fail with bad passphrase", result.success());
Assert.assertTrue("should contain bad passphrase log entry", Assert.assertTrue("should contain bad passphrase log entry",
result.getLog().containsType(LogType.MSG_DC_ERROR_SYM_PASSPHRASE)); result.getLog().containsType(LogType.MSG_DC_ERROR_SYM_PASSPHRASE));
} }
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(passphrase)); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(passphrase));
assertTrue("decryption must succeed with passphrase", result.success()); assertTrue("decryption must succeed with passphrase", result.success());
assertEquals("backup filename should be backup_keyid.pub.asc", assertEquals("backup filename should be backup_keyid.pub.asc",
@@ -49,7 +49,7 @@ public class BenchmarkOperationTest {
BenchmarkOperation op = new BenchmarkOperation(RuntimeEnvironment.application, BenchmarkOperation op = new BenchmarkOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
op.execute(new BenchmarkInputParcel(), null); op.execute(BenchmarkInputParcel.newInstance(), null);
} }
} }
@@ -48,6 +48,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@@ -72,17 +73,17 @@ public class CertifyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("derp"); builder.addUserId("derp");
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -90,23 +91,22 @@ public class CertifyOperationTest {
} }
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("ditz"); builder.addUserId("ditz");
byte[] uatdata = new byte[random.nextInt(150)+10]; byte[] uatdata = new byte[random.nextInt(150)+10];
random.nextBytes(uatdata); random.nextBytes(uatdata);
parcel.mAddUserAttribute.add( builder.addUserAttribute(WrappedUserAttribute.fromSubpacket(random.nextInt(100)+1, uatdata));
WrappedUserAttribute.fromSubpacket(random.nextInt(100)+1, uatdata));
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase2)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(mKeyPhrase2));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -153,10 +153,10 @@ public class CertifyOperationTest {
Certs.UNVERIFIED, ring.getVerified()); Certs.UNVERIFIED, ring.getVerified());
} }
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId()); CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), actions.addAction(CertifyAction.createForUserIds(mStaticRing2.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds(), null)); mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1)); CertifyResult result = op.execute(actions.build(), CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success()); Assert.assertTrue("certification must succeed", result.success());
@@ -181,10 +181,10 @@ public class CertifyOperationTest {
Certs.UNVERIFIED, ring.getVerified()); Certs.UNVERIFIED, ring.getVerified());
} }
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId()); CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), null, actions.addAction(CertifyAction.createForUserAttributes(mStaticRing2.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserAttributes())); mStaticRing2.getPublicKey().getUnorderedUserAttributes()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1)); CertifyResult result = op.execute(actions.build(), CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success()); Assert.assertTrue("certification must succeed", result.success());
@@ -203,11 +203,11 @@ public class CertifyOperationTest {
CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application, CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null);
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId()); CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(), actions.addAction(CertifyAction.createForUserIds(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds(), null)); mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1)); CertifyResult result = op.execute(actions.build(), CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertFalse("certification with itself must fail!", result.success()); Assert.assertFalse("certification with itself must fail!", result.success());
Assert.assertTrue("error msg must be about self certification", Assert.assertTrue("error msg must be about self certification",
@@ -221,12 +221,12 @@ public class CertifyOperationTest {
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null);
{ {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId()); CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId());
ArrayList<String> uids = new ArrayList<String>(); ArrayList<String> uids = new ArrayList<String>();
uids.add("nonexistent"); uids.add("nonexistent");
actions.add(new CertifyAction(1234L, uids, null)); actions.addAction(CertifyAction.createForUserIds(1234L, uids));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), CertifyResult result = op.execute(actions.build(), CryptoInputParcel.createCryptoInputParcel(new Date(),
mKeyPhrase1)); mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success()); Assert.assertFalse("certification of nonexistent key must fail", result.success());
@@ -235,11 +235,11 @@ public class CertifyOperationTest {
} }
{ {
CertifyActionsParcel actions = new CertifyActionsParcel(1234L); CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(1234L);
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(), actions.addAction(CertifyAction.createForUserIds(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds(), null)); mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), CertifyResult result = op.execute(actions.build(), CryptoInputParcel.createCryptoInputParcel(new Date(),
mKeyPhrase1)); mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success()); Assert.assertFalse("certification of nonexistent key must fail", result.success());
@@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel; import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@@ -68,17 +69,17 @@ public class PromoteKeyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("derp"); builder.addUserId("derp");
parcel.setNewUnlock(new ChangeUnlockParcel(mKeyPhrase1)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(mKeyPhrase1));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -106,7 +107,8 @@ public class PromoteKeyOperationTest {
PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application, PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null, null);
PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), null, null), null); PromoteKeyResult result = op.execute(
PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), null, null), null);
Assert.assertTrue("promotion must succeed", result.success()); Assert.assertTrue("promotion must succeed", result.success());
@@ -132,7 +134,8 @@ public class PromoteKeyOperationTest {
byte[] aid = Hex.decode("D2760001240102000000012345670000"); byte[] aid = Hex.decode("D2760001240102000000012345670000");
PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, null), null); PromoteKeyResult result = op.execute(
PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, null), null);
Assert.assertTrue("promotion must succeed", result.success()); Assert.assertTrue("promotion must succeed", result.success());
@@ -160,7 +163,8 @@ public class PromoteKeyOperationTest {
// only promote the first, rest stays dummy // only promote the first, rest stays dummy
long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1); long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1);
PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, new long[] { PromoteKeyResult result = op.execute(
PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, new long[] {
keyId keyId
}), null); }), null);
@@ -128,9 +128,9 @@ public class InputDataOperationTest {
InputDataOperation op = new InputDataOperation(spyApplication, InputDataOperation op = new InputDataOperation(spyApplication,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
InputDataParcel input = new InputDataParcel(fakeInputUri, null); InputDataParcel input = InputDataParcel.createInputDataParcel(fakeInputUri, null);
InputDataResult result = op.execute(input, new CryptoInputParcel()); InputDataResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel());
// must be successful, no verification, have two output URIs // must be successful, no verification, have two output URIs
Assert.assertTrue(result.success()); Assert.assertTrue(result.success());
@@ -308,8 +308,8 @@ public class InputDataOperationTest {
InputDataOperation op = new InputDataOperation(spyApplication, InputDataOperation op = new InputDataOperation(spyApplication,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
InputDataParcel input = new InputDataParcel(FAKE_CONTENT_INPUT_URI_1, null); InputDataParcel input = InputDataParcel.createInputDataParcel(FAKE_CONTENT_INPUT_URI_1, null);
return op.execute(input, new CryptoInputParcel()); return op.execute(input, CryptoInputParcel.createCryptoInputParcel());
} }
} }
@@ -25,7 +25,6 @@ import java.io.PrintStream;
import java.security.Security; import java.security.Security;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import org.apache.tools.ant.util.StringUtils; import org.apache.tools.ant.util.StringUtils;
@@ -178,14 +177,13 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setSymmetricPassphrase(mSymmetricPassphrase); pgpData.setSymmetricPassphrase(mSymmetricPassphrase);
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()), CryptoInputParcel.createCryptoInputParcel(new Date()), data, out);
data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
@@ -200,10 +198,11 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowSymmetricDecryption(true); .setAllowSymmetricDecryption(true)
.build();
DecryptVerifyResult result = op.execute( DecryptVerifyResult result = op.execute(
input, new CryptoInputParcel(mSymmetricPassphrase), data, out); input, CryptoInputParcel.createCryptoInputParcel(mSymmetricPassphrase), data, out);
Assert.assertTrue("decryption must succeed", result.success()); Assert.assertTrue("decryption must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext should equal plaintext",
@@ -230,10 +229,11 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowSymmetricDecryption(true); .setAllowSymmetricDecryption(true)
.build();
DecryptVerifyResult result = op.execute(input, DecryptVerifyResult result = op.execute(input,
new CryptoInputParcel(new Passphrase(new String(mSymmetricPassphrase.getCharArray()) + "x")), CryptoInputParcel.createCryptoInputParcel(new Passphrase(new String(mSymmetricPassphrase.getCharArray()) + "x")),
data, out); data, out);
Assert.assertFalse("decryption must fail", result.success()); Assert.assertFalse("decryption must fail", result.success());
@@ -252,10 +252,11 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowSymmetricDecryption(true); .setAllowSymmetricDecryption(true)
.build();
DecryptVerifyResult result = op.execute(input, DecryptVerifyResult result = op.execute(input,
new CryptoInputParcel(), data, out); CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertFalse("decryption must fail", result.success()); Assert.assertFalse("decryption must fail", result.success());
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size()); Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
@@ -273,10 +274,9 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
input.setAllowSymmetricDecryption(false);
DecryptVerifyResult result = op.execute(input, DecryptVerifyResult result = op.execute(input,
new CryptoInputParcel(), data, out); CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertFalse("decryption must fail", result.success()); Assert.assertFalse("decryption must fail", result.success());
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size()); Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
@@ -303,16 +303,15 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
// only sign, and not as cleartext // only sign, and not as cleartext
pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId()); pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1)); pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
pgpData.setCleartextSignature(false); pgpData.setCleartextSignature(false);
pgpData.setDetachedSignature(false); pgpData.setDetachedSignature(false);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue("signing must succeed", result.success()); Assert.assertTrue("signing must succeed", result.success());
ciphertext = out.toByteArray(); ciphertext = out.toByteArray();
@@ -325,8 +324,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("verification must succeed", result.success()); Assert.assertTrue("verification must succeed", result.success());
Assert.assertArrayEquals("verification text should equal plaintext", Assert.assertArrayEquals("verification text should equal plaintext",
@@ -359,7 +358,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
// only sign, as cleartext // only sign, as cleartext
pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId()); pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1)); pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
@@ -367,9 +366,8 @@ public class PgpEncryptDecryptTest {
pgpData.setEnableAsciiArmorOutput(true); pgpData.setEnableAsciiArmorOutput(true);
pgpData.setDetachedSignature(false); pgpData.setDetachedSignature(false);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue("signing must succeed", result.success()); Assert.assertTrue("signing must succeed", result.success());
ciphertext = out.toByteArray(); ciphertext = out.toByteArray();
@@ -385,8 +383,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("verification must succeed", result.success()); Assert.assertTrue("verification must succeed", result.success());
@@ -421,15 +419,14 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
// only sign, as cleartext // only sign, as cleartext
pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId()); pgpData.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1)); pgpData.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
pgpData.setDetachedSignature(true); pgpData.setDetachedSignature(true);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue("signing must succeed", result.success()); Assert.assertTrue("signing must succeed", result.success());
detachedSignature = result.getDetachedSignature(); detachedSignature = result.getDetachedSignature();
@@ -442,9 +439,10 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setDetachedSignature(detachedSignature); .setDetachedSignature(detachedSignature)
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); .build();
DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("verification must succeed", result.success()); Assert.assertTrue("verification must succeed", result.success());
Assert.assertArrayEquals("verification text should equal plaintext (save for a newline)", Assert.assertArrayEquals("verification text should equal plaintext (save for a newline)",
@@ -478,14 +476,13 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() }); pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date()),
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out); data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
@@ -499,8 +496,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue("decryption with provided passphrase must succeed", result.success()); Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
@@ -528,8 +525,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase1, mStaticRing1.getMasterKeyId(), null); mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel(); CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
Assert.assertEquals("must have one cached session key", Assert.assertEquals("must have one cached session key",
@@ -552,8 +549,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
null, mStaticRing1.getMasterKeyId(), null); null, mStaticRing1.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertFalse("decryption with no passphrase must return pending", result.success()); Assert.assertFalse("decryption with no passphrase must return pending", result.success());
Assert.assertTrue("decryption with no passphrase should return pending", result.isPending()); Assert.assertTrue("decryption with no passphrase should return pending", result.isPending());
@@ -581,14 +578,13 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() }); pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date()),
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out); data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
@@ -621,12 +617,12 @@ public class PgpEncryptDecryptTest {
{ // strip first encrypted subkey, decryption should skip it { // strip first encrypted subkey, decryption should skip it
SaveKeyringParcel parcel = SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildChangeKeyringParcel(
new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint());
parcel.mChangeSubKeys.add(new SubkeyChange(encKeyId1, true, false)); builder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(encKeyId1));
UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1, UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(builder.build(), mStaticRing1,
new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(),
new CryptoInputParcel(new Date(), mKeyPhrase1)); CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
KeyWritableRepository databaseInteractor = KeyWritableRepository databaseInteractor =
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application);
@@ -634,8 +630,10 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(ciphertext); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1)); .setInputBytes(ciphertext)
.build();
DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1));
Assert.assertTrue("decryption must succeed", result.success()); Assert.assertTrue("decryption must succeed", result.success());
Assert.assertTrue("decryption must have skipped first key", Assert.assertTrue("decryption must have skipped first key",
@@ -644,12 +642,12 @@ public class PgpEncryptDecryptTest {
{ // change flags of second encrypted subkey, decryption should skip it { // change flags of second encrypted subkey, decryption should skip it
SaveKeyringParcel parcel = SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildChangeKeyringParcel(
new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint());
parcel.mChangeSubKeys.add(new SubkeyChange(encKeyId1, KeyFlags.CERTIFY_OTHER, null)); builder.addOrReplaceSubkeyChange(SubkeyChange.createFlagsOrExpiryChange(encKeyId1, KeyFlags.CERTIFY_OTHER, null));
UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1, UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(builder.build(), mStaticRing1,
new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(),
new CryptoInputParcel(new Date(), mKeyPhrase1)); CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
KeyWritableRepository databaseInteractor = KeyWritableRepository databaseInteractor =
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application);
@@ -657,8 +655,10 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(ciphertext); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1)); .setInputBytes(ciphertext)
.build();
DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1));
Assert.assertTrue("decryption must succeed", result.success()); Assert.assertTrue("decryption must succeed", result.success());
Assert.assertTrue("decryption must have skipped first key", Assert.assertTrue("decryption must have skipped first key",
@@ -673,11 +673,12 @@ public class PgpEncryptDecryptTest {
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true); String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
{ // revoke first encryption subkey of keyring in database { // revoke first encryption subkey of keyring in database
SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildChangeKeyringParcel(
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(mStaticRing1, 2)); mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint());
UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1, builder.addRevokeSubkey(KeyringTestingHelper.getSubkeyId(mStaticRing1, 2));
UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(builder.build(), mStaticRing1,
new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new ArrayList<RawPacket>(),
new CryptoInputParcel(new Date(), mKeyPhrase1)); CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1));
KeyWritableRepository databaseInteractor = KeyWritableRepository databaseInteractor =
KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application); KeyWritableRepository.createDatabaseReadWriteInteractor(RuntimeEnvironment.application);
@@ -694,14 +695,13 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() }); pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date()),
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out); data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
@@ -739,7 +739,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { pgpData.setEncryptionMasterKeyIds(new long[] {
mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(),
mStaticRing2.getMasterKeyId() mStaticRing2.getMasterKeyId()
@@ -747,9 +747,8 @@ public class PgpEncryptDecryptTest {
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date()),
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
data, out); data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
@@ -764,8 +763,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase1, mStaticRing1.getMasterKeyId(), null); mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success()); Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
@@ -787,15 +786,16 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
// allow only the second to decrypt // allow only the second to decrypt
HashSet<Long> allowed = new HashSet<>(); ArrayList<Long> allowed = new ArrayList<>();
allowed.add(mStaticRing2.getMasterKeyId()); allowed.add(mStaticRing2.getMasterKeyId());
// provide passphrase for the second, and check that the first is never asked for! // provide passphrase for the second, and check that the first is never asked for!
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase2, mStaticRing2.getMasterKeyId(), null); mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowedKeyIds(allowed); .setAllowedKeyIds(allowed)
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); .build();
DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("decryption with cached passphrase must succeed for allowed key", result.success()); Assert.assertTrue("decryption with cached passphrase must succeed for allowed key", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
@@ -816,9 +816,10 @@ public class PgpEncryptDecryptTest {
// provide passphrase for the second, and check that the first is never asked for! // provide passphrase for the second, and check that the first is never asked for!
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase2, mStaticRing2.getMasterKeyId(), null); mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder()
input.setAllowedKeyIds(new HashSet<Long>()); .setAllowedKeyIds(new ArrayList<Long>())
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); .build();
DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertFalse("decryption must fail if no key allowed", result.success()); Assert.assertFalse("decryption must fail if no key allowed", result.success());
Assert.assertEquals("decryption must fail with key disllowed status", Assert.assertEquals("decryption must fail with key disllowed status",
@@ -839,8 +840,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase2, mStaticRing2.getMasterKeyId(), null); mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("decryption with cached passphrase must succeed", result.success()); Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
@@ -868,7 +869,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { pgpData.setEncryptionMasterKeyIds(new long[] {
mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(),
mStaticRing2.getMasterKeyId() mStaticRing2.getMasterKeyId()
@@ -878,10 +879,8 @@ public class PgpEncryptDecryptTest {
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date(), mKeyPhrase1), data, out);
PgpSignEncryptResult result = op.execute(b,
new CryptoInputParcel(new Date(), mKeyPhrase1), data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray(); ciphertext = out.toByteArray();
@@ -895,8 +894,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase1, mStaticRing1.getMasterKeyId(), null); mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success()); Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
@@ -922,8 +921,8 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache( PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
mKeyPhrase2, mStaticRing2.getMasterKeyId(), null); mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue("decryption with cached passphrase must succeed", result.success()); Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
@@ -955,7 +954,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() }); pgpData.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
pgpData.setSymmetricEncryptionAlgorithm( pgpData.setSymmetricEncryptionAlgorithm(
PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128); PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
@@ -963,10 +962,8 @@ public class PgpEncryptDecryptTest {
pgpData.setEnableAsciiArmorOutput(true); pgpData.setEnableAsciiArmorOutput(true);
pgpData.setCharset("iso-2022-jp"); pgpData.setCharset("iso-2022-jp");
PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptResult result = op.execute(pgpData.build(),
CryptoInputParcel.createCryptoInputParcel(new Date()), data, out);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success()); Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray(); ciphertext = out.toByteArray();
@@ -979,8 +976,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue("decryption with provided passphrase must succeed", result.success()); Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes", Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes",
@@ -1007,8 +1004,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue(result.success()); Assert.assertTrue(result.success());
@@ -1030,8 +1027,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(mKeyPhrase1), data, out);
Assert.assertTrue(result.success()); Assert.assertTrue(result.success());
@@ -1051,8 +1048,8 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null); PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out); DecryptVerifyResult result = op.execute(input, CryptoInputParcel.createCryptoInputParcel(), data, out);
Assert.assertTrue(result.success()); Assert.assertTrue(result.success());
@@ -1106,7 +1103,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available()); InputData data = new InputData(in, in.available());
PgpSignEncryptData pgpData = new PgpSignEncryptData(); PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder();
pgpData.setEncryptionMasterKeyIds(new long[]{ mStaticRingInsecure.getMasterKeyId()}); pgpData.setEncryptionMasterKeyIds(new long[]{ mStaticRingInsecure.getMasterKeyId()});
PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData); PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel(pgpData);
@@ -18,8 +18,20 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import junit.framework.AssertionFailedError;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import junit.framework.AssertionFailedError;
import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.Packet; import org.bouncycastle.bcpg.Packet;
import org.bouncycastle.bcpg.PacketTags; import org.bouncycastle.bcpg.PacketTags;
@@ -58,16 +70,18 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
import java.io.ByteArrayInputStream; import static org.bouncycastle.bcpg.sig.KeyFlags.CERTIFY_OTHER;
import java.io.IOException; import static org.bouncycastle.bcpg.sig.KeyFlags.SIGN_DATA;
import java.nio.ByteBuffer; import static org.sufficientlysecure.keychain.operations.results.OperationResult.LogType.MSG_MF_ERROR_FINGERPRINT;
import java.security.Security; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm.ECDSA;
import java.util.ArrayList; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm.RSA;
import java.util.Date; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve.NIST_P256;
import java.util.HashSet; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd.createSubkeyAdd;
import java.util.Iterator; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange.createFlagsOrExpiryChange;
import java.util.List; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange.createRecertifyChange;
import java.util.Random; import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange.createStripChange;
import static org.sufficientlysecure.keychain.service.SaveKeyringParcel.buildChangeKeyringParcel;
@RunWith(KeychainTestRunner.class) @RunWith(KeychainTestRunner.class)
public class PgpKeyOperationTest { public class PgpKeyOperationTest {
@@ -77,7 +91,7 @@ public class PgpKeyOperationTest {
UncachedKeyRing ring; UncachedKeyRing ring;
PgpKeyOperation op; PgpKeyOperation op;
SaveKeyringParcel parcel; SaveKeyringParcel.Builder builder;
ArrayList<RawPacket> onlyA = new ArrayList<>(); ArrayList<RawPacket> onlyA = new ArrayList<>();
ArrayList<RawPacket> onlyB = new ArrayList<>(); ArrayList<RawPacket> onlyB = new ArrayList<>();
@@ -88,28 +102,28 @@ public class PgpKeyOperationTest {
Security.insertProviderAt(new BouncyCastleProvider(), 1); Security.insertProviderAt(new BouncyCastleProvider(), 1);
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); builder.addUserId("twi");
parcel.mAddUserIds.add("pink"); builder.addUserId("pink");
{ {
int type = 42; int type = 42;
byte[] data = new byte[] { 0, 1, 2, 3, 4 }; byte[] data = new byte[] { 0, 1, 2, 3, 4 };
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data); WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data);
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
} }
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing()); Assert.assertNotNull("initial test key creation must succeed", result.getRing());
@@ -119,11 +133,12 @@ public class PgpKeyOperationTest {
// we sleep here for a second, to make sure all new certificates have different timestamps // we sleep here for a second, to make sure all new certificates have different timestamps
Thread.sleep(1000); Thread.sleep(1000);
cryptoInput = new CryptoInputParcel(new Date(), passphrase); cryptoInput = CryptoInputParcel.createCryptoInputParcel(new Date(), passphrase);
} }
@Before public void setUp() throws Exception { @Before
public void setUp() throws Exception {
// show Log.x messages in system.out // show Log.x messages in system.out
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
ring = staticRing; ring = staticRing;
@@ -131,76 +146,76 @@ public class PgpKeyOperationTest {
// setting up some parameters just to reduce code duplication // setting up some parameters just to reduce code duplication
op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100)); op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
// set this up, gonna need it more than once resetBuilder();
parcel = new SaveKeyringParcel(); }
parcel.mMasterKeyId = ring.getMasterKeyId();
parcel.mFingerprint = ring.getFingerprint();
private void resetBuilder() {
builder = SaveKeyringParcel.buildChangeKeyringParcel(ring.getMasterKeyId(), ring.getFingerprint());
} }
@Test @Test
public void createSecretKeyRingTests() { public void createSecretKeyRingTests() {
{ {
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); builder.addUserId("shy");
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating ring with < 2048 bit keysize should fail", parcel, assertFailure("creating ring with < 2048 bit keysize should fail", builder.build(),
LogType.MSG_CR_ERROR_KEYSIZE_2048); LogType.MSG_CR_ERROR_KEYSIZE_2048);
} }
{ {
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ELGAMAL, 2048, null, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ELGAMAL, 2048, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); builder.addUserId("shy");
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating ring with ElGamal master key should fail", parcel, assertFailure("creating ring with ElGamal master key should fail", builder.build(),
LogType.MSG_CR_ERROR_FLAGS_ELGAMAL); LogType.MSG_CR_ERROR_FLAGS_ELGAMAL);
} }
{ {
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, null)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, null));
parcel.mAddUserIds.add("lotus"); builder.addUserId("lotus");
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating master key with null expiry should fail", parcel, assertFailure("creating master key with null expiry should fail", builder.build(),
LogType.MSG_CR_ERROR_NULL_EXPIRY); LogType.MSG_CR_ERROR_NULL_EXPIRY);
} }
{ {
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("shy"); builder.addUserId("shy");
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating ring with non-certifying master key should fail", parcel, assertFailure("creating ring with non-certifying master key should fail", builder.build(),
LogType.MSG_CR_ERROR_NO_CERTIFY); LogType.MSG_CR_ERROR_NO_CERTIFY);
} }
{ {
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating ring without user ids should fail", parcel, assertFailure("creating ring without user ids should fail", builder.build(),
LogType.MSG_CR_ERROR_NO_USER_ID); LogType.MSG_CR_ERROR_NO_USER_ID);
} }
{ {
parcel.reset(); resetBuilder();
parcel.mAddUserIds.add("shy"); builder.addUserId("shy");
parcel.setNewUnlock(new ChangeUnlockParcel(passphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
assertFailure("creating ring with no master key should fail", parcel, assertFailure("creating ring with no master key should fail", builder.build(),
LogType.MSG_CR_ERROR_NO_MASTER); LogType.MSG_CR_ERROR_NO_MASTER);
} }
@@ -210,11 +225,11 @@ public class PgpKeyOperationTest {
// this is a special case since the flags are in user id certificates rather than // this is a special case since the flags are in user id certificates rather than
// subkey binding certificates // subkey binding certificates
public void testMasterFlags() throws Exception { public void testMasterFlags() throws Exception {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("luna"); builder.addUserId("luna");
ring = assertCreateSuccess("creating ring with master key flags must succeed", parcel); ring = assertCreateSuccess("creating ring with master key flags must succeed", builder.build());
Assert.assertEquals("the keyring should contain only the master key", Assert.assertEquals("the keyring should contain only the master key",
1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size()); 1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
@@ -280,43 +295,27 @@ public class PgpKeyOperationTest {
public void testBadKeyModification() throws Exception { public void testBadKeyModification() throws Exception {
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildChangeKeyringParcel(
// off by one ring.getMasterKeyId() -1, ring.getFingerprint());
parcel.mMasterKeyId = ring.getMasterKeyId() -1;
parcel.mFingerprint = ring.getFingerprint();
assertModifyFailure("keyring modification with bad master key id should fail", assertModifyFailure("keyring modification with bad master key id should fail",
ring, parcel, LogType.MSG_MF_ERROR_KEYID); ring, builder.build(), LogType.MSG_MF_ERROR_KEYID);
} }
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); byte[] fingerprint = Arrays.copyOf(ring.getFingerprint(), ring.getFingerprint().length);
// off by one fingerprint[5] += 1;
parcel.mMasterKeyId = null;
parcel.mFingerprint = ring.getFingerprint();
assertModifyFailure("keyring modification with null master key id should fail", SaveKeyringParcel.Builder builder = buildChangeKeyringParcel(ring.getMasterKeyId(), fingerprint);
ring, parcel, LogType.MSG_MF_ERROR_KEYID);
}
{
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mMasterKeyId = ring.getMasterKeyId();
parcel.mFingerprint = ring.getFingerprint();
// some byte, off by one
parcel.mFingerprint[5] += 1;
assertModifyFailure("keyring modification with bad fingerprint should fail", assertModifyFailure("keyring modification with bad fingerprint should fail",
ring, parcel, LogType.MSG_MF_ERROR_FINGERPRINT); ring, builder.build(), MSG_MF_ERROR_FINGERPRINT);
} }
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = buildChangeKeyringParcel(ring.getMasterKeyId(), null);
parcel.mMasterKeyId = ring.getMasterKeyId();
parcel.mFingerprint = null;
assertModifyFailure("keyring modification with null fingerprint should fail", assertModifyFailure("keyring modification with null fingerprint should fail",
ring, parcel, LogType.MSG_MF_ERROR_FINGERPRINT); ring, builder.build(), MSG_MF_ERROR_FINGERPRINT);
} }
{ {
@@ -324,16 +323,16 @@ public class PgpKeyOperationTest {
if (badphrase.equals(passphrase)) { if (badphrase.equals(passphrase)) {
badphrase = new Passphrase("a"); badphrase = new Passphrase("a");
} }
parcel.mAddUserIds.add("allure"); builder.addUserId("allure");
assertModifyFailure("keyring modification with bad passphrase should fail", assertModifyFailure("keyring modification with bad passphrase should fail",
ring, parcel, new CryptoInputParcel(badphrase), LogType.MSG_MF_UNLOCK_ERROR); ring, builder.build(), CryptoInputParcel.createCryptoInputParcel(badphrase), LogType.MSG_MF_UNLOCK_ERROR);
} }
{ {
parcel.reset(); resetBuilder();
assertModifyFailure("no-op should fail", assertModifyFailure("no-op should fail",
ring, parcel, cryptoInput, LogType.MSG_MF_ERROR_NOOP); ring, builder.build(), cryptoInput, LogType.MSG_MF_ERROR_NOOP);
} }
} }
@@ -343,10 +342,10 @@ public class PgpKeyOperationTest {
long expiry = new Date().getTime() / 1000 + 159; long expiry = new Date().getTime() / 1000 + 159;
int flags = KeyFlags.SIGN_DATA; int flags = KeyFlags.SIGN_DATA;
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, flags, expiry)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, flags, expiry));
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); UncachedKeyRing modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertEquals("no extra packets in original", 0, onlyA.size()); Assert.assertEquals("no extra packets in original", 0, onlyA.size());
Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size()); Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
@@ -381,26 +380,27 @@ public class PgpKeyOperationTest {
flags, (long) newKey.getKeyUsage()); flags, (long) newKey.getKeyUsage());
{ // bad keysize should fail { // bad keysize should fail
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SubkeyAdd( builder.addSubkeyAdd(createSubkeyAdd(
Algorithm.RSA, new Random().nextInt(512), null, KeyFlags.SIGN_DATA, 0L)); RSA, new Random().nextInt(512), null, SIGN_DATA, 0L));
assertModifyFailure("creating a subkey with keysize < 2048 should fail", ring, parcel, assertModifyFailure("creating a subkey with keysize < 2048 should fail", ring, builder.build(),
LogType.MSG_CR_ERROR_KEYSIZE_2048); LogType.MSG_CR_ERROR_KEYSIZE_2048);
} }
{ // null expiry should fail { // null expiry should fail
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, null)); ECDSA, 0, NIST_P256, SIGN_DATA, null));
assertModifyFailure("creating master key with null expiry should fail", ring, parcel, assertModifyFailure("creating master key with null expiry should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_NULL_EXPIRY); LogType.MSG_MF_ERROR_NULL_EXPIRY);
} }
{ // a past expiry should fail { // a past expiry should fail
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, new Date().getTime()/1000-10)); ECDSA, 0, NIST_P256, SIGN_DATA,
assertModifyFailure("creating subkey with past expiry date should fail", ring, parcel, new Date().getTime() / 1000 - 10));
assertModifyFailure("creating subkey with past expiry date should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_PAST_EXPIRY); LogType.MSG_MF_ERROR_PAST_EXPIRY);
} }
@@ -414,8 +414,8 @@ public class PgpKeyOperationTest {
UncachedKeyRing modified = ring; UncachedKeyRing modified = ring;
{ {
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, expiry));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size()); Assert.assertEquals("one extra packet in original", 1, onlyA.size());
Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
@@ -441,8 +441,8 @@ public class PgpKeyOperationTest {
{ // change expiry { // change expiry
expiry += 60*60*24; expiry += 60*60*24;
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, expiry));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertNotNull("modified key must have an expiry date", Assert.assertNotNull("modified key must have an expiry date",
modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting()); modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());
@@ -454,9 +454,9 @@ public class PgpKeyOperationTest {
{ {
int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS; int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS;
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, flags, null));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("old packet must be signature", Assert.assertEquals("old packet must be signature",
PacketTags.SIGNATURE, onlyA.get(0).tag); PacketTags.SIGNATURE, onlyA.get(0).tag);
@@ -477,9 +477,9 @@ public class PgpKeyOperationTest {
} }
{ // expiry of 0 should be "no expiry" { // expiry of 0 should be "no expiry"
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, 0L)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, 0L));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("old packet must be signature", Assert.assertEquals("old packet must be signature",
PacketTags.SIGNATURE, onlyA.get(0).tag); PacketTags.SIGNATURE, onlyA.get(0).tag);
@@ -495,18 +495,18 @@ public class PgpKeyOperationTest {
} }
{ // a past expiry should fail { // a past expiry should fail
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, new Date().getTime() / 1000 - 10));
assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel, assertModifyFailure("setting subkey expiry to a past date should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_PAST_EXPIRY); LogType.MSG_MF_ERROR_PAST_EXPIRY);
} }
{ // modifying nonexistent subkey should fail { // modifying nonexistent subkey should fail
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(123, null, null));
assertModifyFailure("modifying non-existent subkey should fail", ring, parcel, assertModifyFailure("modifying non-existent subkey should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_SUBKEY_MISSING); LogType.MSG_MF_ERROR_SUBKEY_MISSING);
} }
@@ -521,15 +521,15 @@ public class PgpKeyOperationTest {
UncachedKeyRing modified = ring; UncachedKeyRing modified = ring;
// to make this check less trivial, we add a user id, change the primary one and revoke one // to make this check less trivial, we add a user id, change the primary one and revoke one
parcel.mAddUserIds.add("aloe"); builder.addUserId("aloe");
parcel.mChangePrimaryUserId = "aloe"; builder.setChangePrimaryUserId("aloe");
parcel.mRevokeUserIds.add("pink"); builder.addRevokeUserId("pink");
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
{ {
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, expiry));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
// this implies that only the two non-revoked signatures were changed! // this implies that only the two non-revoked signatures were changed!
Assert.assertEquals("two extra packets in original", 2, onlyA.size()); Assert.assertEquals("two extra packets in original", 2, onlyA.size());
@@ -555,8 +555,8 @@ public class PgpKeyOperationTest {
{ // change expiry { // change expiry
expiry += 60*60*24; expiry += 60*60*24;
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, expiry));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertNotNull("modified key must have an expiry date", Assert.assertNotNull("modified key must have an expiry date",
modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting()); modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());
@@ -574,9 +574,9 @@ public class PgpKeyOperationTest {
{ {
int flags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; int flags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, flags, null));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("modified key must have expected flags", Assert.assertEquals("modified key must have expected flags",
flags, (long) modified.getPublicKey(keyId).getKeyUsage()); flags, (long) modified.getPublicKey(keyId).getKeyUsage());
@@ -590,13 +590,13 @@ public class PgpKeyOperationTest {
// even if there is a non-expiring user id while all others are revoked, it doesn't count! // even if there is a non-expiring user id while all others are revoked, it doesn't count!
// for this purpose we revoke one while they still have expiry times // for this purpose we revoke one while they still have expiry times
parcel.reset(); resetBuilder();
parcel.mRevokeUserIds.add("aloe"); builder.addRevokeUserId("aloe");
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, 0L)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, 0L));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
// for this check, it is relevant that we DON'T use the unsafe one! // for this check, it is relevant that we DON'T use the unsafe one!
Assert.assertNull("key must not expire anymore", Assert.assertNull("key must not expire anymore",
@@ -607,28 +607,28 @@ public class PgpKeyOperationTest {
} }
{ // if we revoke everything, nothing is left to properly sign... { // if we revoke everything, nothing is left to properly sign...
parcel.reset(); resetBuilder();
parcel.mRevokeUserIds.add("twi"); builder.addRevokeUserId("twi");
parcel.mRevokeUserIds.add("pink"); builder.addRevokeUserId("pink");
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.CERTIFY_OTHER, null)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, CERTIFY_OTHER, null));
assertModifyFailure("master key modification with all user ids revoked should fail", ring, parcel, assertModifyFailure("master key modification with all user ids revoked should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_MASTER_NONE); LogType.MSG_MF_ERROR_MASTER_NONE);
} }
{ // any flag not including CERTIFY_OTHER should fail { // any flag not including CERTIFY_OTHER should fail
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.SIGN_DATA, null)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, SIGN_DATA, null));
assertModifyFailure("setting master key flags without certify should fail", ring, parcel, assertModifyFailure("setting master key flags without certify should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_NO_CERTIFY); LogType.MSG_MF_ERROR_NO_CERTIFY);
} }
{ // a past expiry should fail { // a past expiry should fail
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10)); builder.addOrReplaceSubkeyChange(createFlagsOrExpiryChange(keyId, null, new Date().getTime() / 1000 - 10));
assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel, assertModifyFailure("setting subkey expiry to a past date should fail", ring, builder.build(),
LogType.MSG_MF_ERROR_PAST_EXPIRY); LogType.MSG_MF_ERROR_PAST_EXPIRY);
} }
@@ -637,10 +637,10 @@ public class PgpKeyOperationTest {
@Test @Test
public void testMasterRevoke() throws Exception { public void testMasterRevoke() throws Exception {
parcel.reset(); resetBuilder();
parcel.mRevokeSubKeys.add(ring.getMasterKeyId()); builder.addRevokeSubkey(ring.getMasterKeyId());
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); UncachedKeyRing modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertEquals("no extra packets in original", 0, onlyA.size()); Assert.assertEquals("no extra packets in original", 0, onlyA.size());
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
@@ -669,11 +669,11 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); resetBuilder();
parcel.mRevokeSubKeys.add(123L); builder.addRevokeSubkey(123L);
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0);
UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, cryptoInput, parcel).getRing(); UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, cryptoInput, builder.build()).getRing();
Assert.assertNull("revoking a nonexistent subkey should fail", otherModified); Assert.assertNull("revoking a nonexistent subkey should fail", otherModified);
@@ -681,11 +681,11 @@ public class PgpKeyOperationTest {
{ // revoked second subkey { // revoked second subkey
parcel.reset(); resetBuilder();
parcel.mRevokeSubKeys.add(keyId); builder.addRevokeSubkey(keyId);
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB,
new CryptoInputParcel(new Date(), passphrase)); CryptoInputParcel.createCryptoInputParcel(new Date(), passphrase));
Assert.assertEquals("no extra packets in original", 0, onlyA.size()); Assert.assertEquals("no extra packets in original", 0, onlyA.size());
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
@@ -705,11 +705,11 @@ public class PgpKeyOperationTest {
{ // re-add second subkey { // re-add second subkey
parcel.reset(); resetBuilder();
// re-certify the revoked subkey // re-certify the revoked subkey
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true)); builder.addOrReplaceSubkeyChange(createRecertifyChange(keyId, true));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size()); Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
@@ -749,8 +749,8 @@ public class PgpKeyOperationTest {
public void testSubkeyStrip() throws Exception { public void testSubkeyStrip() throws Exception {
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false)); builder.addOrReplaceSubkeyChange(createStripChange(keyId));
applyModificationWithChecks(parcel, ring, onlyA, onlyB); applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size()); Assert.assertEquals("one extra packet in original", 1, onlyA.size());
Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
@@ -775,8 +775,8 @@ public class PgpKeyOperationTest {
public void testMasterStrip() throws Exception { public void testMasterStrip() throws Exception {
long keyId = ring.getMasterKeyId(); long keyId = ring.getMasterKeyId();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false)); builder.addOrReplaceSubkeyChange(createStripChange(keyId));
applyModificationWithChecks(parcel, ring, onlyA, onlyB); applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size()); Assert.assertEquals("one extra packet in original", 1, onlyA.size());
Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
@@ -803,9 +803,10 @@ public class PgpKeyOperationTest {
UncachedKeyRing modified; UncachedKeyRing modified;
{ // we should be able to change the stripped status of subkeys without passphrase { // we should be able to change the stripped status of subkeys without passphrase
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false)); builder.addOrReplaceSubkeyChange(createStripChange(keyId));
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, new CryptoInputParcel()); modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB,
CryptoInputParcel.createCryptoInputParcel());
Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket(); Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
Assert.assertEquals("new packet should have GNU_DUMMY S2K type", Assert.assertEquals("new packet should have GNU_DUMMY S2K type",
@@ -815,11 +816,11 @@ public class PgpKeyOperationTest {
} }
{ // trying to edit a subkey with signing capability should fail { // trying to edit a subkey with signing capability should fail
parcel.reset(); resetBuilder();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true)); builder.addOrReplaceSubkeyChange(createRecertifyChange(keyId, true));
assertModifyFailure("subkey modification for signing-enabled but stripped subkey should fail", assertModifyFailure("subkey modification for signing-enabled but stripped subkey should fail",
modified, parcel, LogType.MSG_MF_ERROR_SUB_STRIPPED); modified, builder.build(), LogType.MSG_MF_ERROR_SUB_STRIPPED);
} }
} }
@@ -828,51 +829,49 @@ public class PgpKeyOperationTest {
public void testKeyToSecurityToken() throws Exception { public void testKeyToSecurityToken() throws Exception {
// Special keyring for security token tests with 2048 bit RSA as a subkey // Special keyring for security token tests with 2048 bit RSA as a subkey
SaveKeyringParcel parcelKey = new SaveKeyringParcel(); SaveKeyringParcel.Builder keyBuilder = SaveKeyringParcel.buildNewKeyringParcel();
parcelKey.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( keyBuilder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.DSA, 2048, null, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.DSA, 2048, null, KeyFlags.CERTIFY_OTHER, 0L));
parcelKey.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( keyBuilder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.RSA, 2048, null, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 2048, null, KeyFlags.SIGN_DATA, 0L));
parcelKey.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( keyBuilder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.RSA, 3072, null, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.RSA, 3072, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcelKey.mAddUserIds.add("yubikey"); keyBuilder.addUserId("yubikey");
parcelKey.setNewUnlock(new ChangeUnlockParcel(passphrase)); keyBuilder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(passphrase));
PgpKeyOperation opSecurityToken = new PgpKeyOperation(null); PgpKeyOperation opSecurityToken = new PgpKeyOperation(null);
PgpEditKeyResult resultSecurityToken = opSecurityToken.createSecretKeyRing(parcelKey); PgpEditKeyResult resultSecurityToken = opSecurityToken.createSecretKeyRing(keyBuilder.build());
Assert.assertTrue("initial test key creation must succeed", resultSecurityToken.success()); Assert.assertTrue("initial test key creation must succeed", resultSecurityToken.success());
Assert.assertNotNull("initial test key creation must succeed", resultSecurityToken.getRing()); Assert.assertNotNull("initial test key creation must succeed", resultSecurityToken.getRing());
UncachedKeyRing ringSecurityToken = resultSecurityToken.getRing(); UncachedKeyRing ringSecurityToken = resultSecurityToken.getRing();
SaveKeyringParcel parcelSecurityToken = new SaveKeyringParcel();
parcelSecurityToken.mMasterKeyId = ringSecurityToken.getMasterKeyId();
parcelSecurityToken.mFingerprint = ringSecurityToken.getFingerprint();
UncachedKeyRing modified; UncachedKeyRing modified;
{ // moveKeyToSecurityToken should fail with BAD_NFC_ALGO when presented with the DSA-1024 key { // moveKeyToSecurityToken should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 0); long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 0);
parcelSecurityToken.reset(); SaveKeyringParcel.Builder securityTokenBuilder = SaveKeyringParcel.buildChangeKeyringParcel(
parcelSecurityToken.mChangeSubKeys.add(new SubkeyChange(keyId, false, true)); ringSecurityToken.getMasterKeyId(), ringSecurityToken.getFingerprint());
securityTokenBuilder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(keyId));
assertModifyFailure("moveKeyToSecurityToken operation should fail on invalid key algorithm", ringSecurityToken, assertModifyFailure("moveKeyToSecurityToken operation should fail on invalid key algorithm", ringSecurityToken,
parcelSecurityToken, cryptoInput, LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO); securityTokenBuilder.build(), cryptoInput, LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO);
} }
long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 1); long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 1);
{ // moveKeyToSecurityToken should return a pending SECURITY_TOKEN_MOVE_KEY_TO_CARD result when presented with the RSA-2048 { // moveKeyToSecurityToken should return a pending SECURITY_TOKEN_MOVE_KEY_TO_CARD result when presented with the RSA-2048
// key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel. // key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
parcelSecurityToken.reset(); SaveKeyringParcel.Builder securityTokenBuilder = SaveKeyringParcel.buildChangeKeyringParcel(
parcelSecurityToken.mChangeSubKeys.add(new SubkeyChange(keyId, false, true)); ringSecurityToken.getMasterKeyId(), ringSecurityToken.getFingerprint());
securityTokenBuilder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(keyId));
CanonicalizedSecretKeyRing secretRing = CanonicalizedSecretKeyRing secretRing =
new CanonicalizedSecretKeyRing(ringSecurityToken.getEncoded(), 0); new CanonicalizedSecretKeyRing(ringSecurityToken.getEncoded(), 0);
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcelSecurityToken); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, securityTokenBuilder.build());
Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending()); Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending());
Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD", Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD",
result.getRequiredInputParcel().mType, RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD); result.getRequiredInputParcel().mType, RequiredInputType.SECURITY_TOKEN_MOVE_KEY_TO_CARD);
@@ -885,10 +884,10 @@ public class PgpKeyOperationTest {
0x6a, 0x6f, 0x6c, 0x6f, 0x73, 0x77, 0x61, 0x67, 0x6a, 0x6f, 0x6c, 0x6f, 0x73, 0x77, 0x61, 0x67,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}; };
CryptoInputParcel inputParcel = new CryptoInputParcel(); CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel();
inputParcel.addCryptoData(keyIdBytes, serial); inputParcel = inputParcel.withCryptoData(keyIdBytes, serial);
modified = applyModificationWithChecks(parcelSecurityToken, ringSecurityToken, onlyA, onlyB, inputParcel); modified = applyModificationWithChecks(securityTokenBuilder.build(), ringSecurityToken, onlyA, onlyB, inputParcel);
Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket(); Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
Assert.assertEquals("new packet should have GNU_DUMMY S2K type", Assert.assertEquals("new packet should have GNU_DUMMY S2K type",
@@ -900,13 +899,14 @@ public class PgpKeyOperationTest {
} }
{ // editing a signing subkey requires a primary key binding sig -> pendinginput { // editing a signing subkey requires a primary key binding sig -> pendinginput
parcelSecurityToken.reset(); SaveKeyringParcel.Builder securityTokenBuilder = SaveKeyringParcel.buildChangeKeyringParcel(
parcelSecurityToken.mChangeSubKeys.add(new SubkeyChange(keyId, true)); ringSecurityToken.getMasterKeyId(), ringSecurityToken.getFingerprint());
securityTokenBuilder.addOrReplaceSubkeyChange(SubkeyChange.createRecertifyChange(keyId, true));
CanonicalizedSecretKeyRing secretRing = CanonicalizedSecretKeyRing secretRing =
new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); new CanonicalizedSecretKeyRing(modified.getEncoded(), 0);
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcelSecurityToken); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, securityTokenBuilder.build());
Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending()); Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending());
Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_SIGN", Assert.assertEquals("required input should be RequiredInputType.SECURITY_TOKEN_SIGN",
RequiredInputType.SECURITY_TOKEN_SIGN, result.getRequiredInputParcel().mType); RequiredInputType.SECURITY_TOKEN_SIGN, result.getRequiredInputParcel().mType);
@@ -921,10 +921,8 @@ public class PgpKeyOperationTest {
String uid = ring.getPublicKey().getUnorderedUserIds().get(1); String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
{ // revoke second user id { // revoke second user id
builder.addRevokeUserId(uid);
parcel.mRevokeUserIds.add(uid); modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
Assert.assertEquals("no extra packets in original", 0, onlyA.size()); Assert.assertEquals("no extra packets in original", 0, onlyA.size());
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
@@ -942,20 +940,20 @@ public class PgpKeyOperationTest {
{ // re-add second user id { // re-add second user id
parcel.reset(); resetBuilder();
parcel.mChangePrimaryUserId = uid; builder.setChangePrimaryUserId(uid);
assertModifyFailure("setting primary user id to a revoked user id should fail", modified, parcel, assertModifyFailure("setting primary user id to a revoked user id should fail", modified, builder.build(),
LogType.MSG_MF_ERROR_REVOKED_PRIMARY); LogType.MSG_MF_ERROR_REVOKED_PRIMARY);
} }
{ // re-add second user id { // re-add second user id
parcel.reset(); resetBuilder();
parcel.mAddUserIds.add(uid); builder.addUserId(uid);
applyModificationWithChecks(parcel, modified, onlyA, onlyB); applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size()); Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
@@ -985,10 +983,10 @@ public class PgpKeyOperationTest {
} }
{ // revocation of non-existent user id should fail { // revocation of non-existent user id should fail
parcel.reset(); resetBuilder();
parcel.mRevokeUserIds.add("nonexistent"); builder.addRevokeUserId("nonexistent");
assertModifyFailure("revocation of nonexistent user id should fail", modified, parcel, assertModifyFailure("revocation of nonexistent user id should fail", modified, builder.build(),
LogType.MSG_MF_ERROR_NOEXIST_REVOKE); LogType.MSG_MF_ERROR_NOEXIST_REVOKE);
} }
@@ -998,15 +996,15 @@ public class PgpKeyOperationTest {
public void testUserIdAdd() throws Exception { public void testUserIdAdd() throws Exception {
{ {
parcel.mAddUserIds.add(""); builder.addUserId("");
assertModifyFailure("adding an empty user id should fail", ring, parcel, assertModifyFailure("adding an empty user id should fail", ring, builder.build(),
LogType.MSG_MF_UID_ERROR_EMPTY); LogType.MSG_MF_UID_ERROR_EMPTY);
} }
parcel.reset(); resetBuilder();
parcel.mAddUserIds.add("rainbow"); builder.addUserId("rainbow");
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); UncachedKeyRing modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertTrue("keyring must contain added user id", Assert.assertTrue("keyring must contain added user id",
modified.getPublicKey().getUnorderedUserIds().contains("rainbow")); modified.getPublicKey().getUnorderedUserIds().contains("rainbow"));
@@ -1035,12 +1033,12 @@ public class PgpKeyOperationTest {
public void testUserAttributeAdd() throws Exception { public void testUserAttributeAdd() throws Exception {
{ {
parcel.mAddUserAttribute.add(WrappedUserAttribute.fromData(new byte[0])); builder.addUserAttribute(WrappedUserAttribute.fromData(new byte[0]));
assertModifyFailure("adding an empty user attribute should fail", ring, parcel, assertModifyFailure("adding an empty user attribute should fail", ring, builder.build(),
LogType.MSG_MF_UAT_ERROR_EMPTY); LogType.MSG_MF_UAT_ERROR_EMPTY);
} }
parcel.reset(); resetBuilder();
Random r = new Random(); Random r = new Random();
int type = r.nextInt(110)+2; // any type except image attribute, to avoid interpretation of these int type = r.nextInt(110)+2; // any type except image attribute, to avoid interpretation of these
@@ -1048,9 +1046,9 @@ public class PgpKeyOperationTest {
new Random().nextBytes(data); new Random().nextBytes(data);
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data); WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data);
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); UncachedKeyRing modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB);
Assert.assertEquals("no extra packets in original", 0, onlyA.size()); Assert.assertEquals("no extra packets in original", 0, onlyA.size());
Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size()); Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
@@ -1081,8 +1079,8 @@ public class PgpKeyOperationTest {
// applying the same modification AGAIN should not add more certifications but drop those // applying the same modification AGAIN should not add more certifications but drop those
// as duplicates // as duplicates
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, applyModificationWithChecks(builder.build(), modified, onlyA, onlyB,
new CryptoInputParcel(new Date(), passphrase), true, false); CryptoInputParcel.createCryptoInputParcel(new Date(), passphrase), true, false);
Assert.assertEquals("duplicate modification: one extra packet in original", 1, onlyA.size()); Assert.assertEquals("duplicate modification: one extra packet in original", 1, onlyA.size());
Assert.assertEquals("duplicate modification: one extra packet in modified", 1, onlyB.size()); Assert.assertEquals("duplicate modification: one extra packet in modified", 1, onlyB.size());
@@ -1102,20 +1100,20 @@ public class PgpKeyOperationTest {
String uid = ring.getPublicKey().getUnorderedUserIds().get(1); String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
{ // first part, add new user id which is also primary { // first part, add new user id which is also primary
parcel.mAddUserIds.add("jack"); builder.addUserId("jack");
parcel.mChangePrimaryUserId = "jack"; builder.setChangePrimaryUserId("jack");
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("primary user id must be the one added", Assert.assertEquals("primary user id must be the one added",
"jack", modified.getPublicKey().getPrimaryUserId()); "jack", modified.getPublicKey().getPrimaryUserId());
} }
{ // second part, change primary to a different one { // second part, change primary to a different one
parcel.reset(); resetBuilder();
parcel.mChangePrimaryUserId = uid; builder.setChangePrimaryUserId(uid);
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB);
Assert.assertEquals("old keyring must have two outdated certificates", 2, onlyA.size()); Assert.assertEquals("old keyring must have two outdated certificates", 2, onlyA.size());
Assert.assertEquals("new keyring must have two new packets", 2, onlyB.size()); Assert.assertEquals("new keyring must have two new packets", 2, onlyB.size());
@@ -1125,15 +1123,11 @@ public class PgpKeyOperationTest {
} }
{ // third part, change primary to a non-existent one { // third part, change primary to a non-existent one
parcel.reset(); resetBuilder();
//noinspection SpellCheckingInspection //noinspection SpellCheckingInspection
parcel.mChangePrimaryUserId = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; builder.setChangePrimaryUserId("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
if (parcel.mChangePrimaryUserId.equals(passphrase)) {
parcel.mChangePrimaryUserId += "A";
}
assertModifyFailure("changing primary user id to a non-existent one should fail", assertModifyFailure("changing primary user id to a non-existent one should fail",
ring, parcel, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY); ring, builder.build(), LogType.MSG_MF_ERROR_NOEXIST_PRIMARY);
} }
// check for revoked primary user id already done in revoke test // check for revoked primary user id already done in revoke test
@@ -1144,9 +1138,9 @@ public class PgpKeyOperationTest {
public void testPassphraseChange() throws Exception { public void testPassphraseChange() throws Exception {
// change passphrase to empty // change passphrase to empty
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
// note that canonicalization here necessarily strips the empty notation packet // note that canonicalization here necessarily strips the empty notation packet
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, cryptoInput); UncachedKeyRing modified = applyModificationWithChecks(builder.build(), ring, onlyA, onlyB, cryptoInput);
Assert.assertEquals("exactly three packets should have been modified (the secret keys)", Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
3, onlyB.size()); 3, onlyB.size());
@@ -1158,16 +1152,16 @@ public class PgpKeyOperationTest {
// modify keyring, change to non-empty passphrase // modify keyring, change to non-empty passphrase
Passphrase otherPassphrase = TestingUtils.genPassphrase(true); Passphrase otherPassphrase = TestingUtils.genPassphrase(true);
CryptoInputParcel otherCryptoInput = new CryptoInputParcel(otherPassphrase); CryptoInputParcel otherCryptoInput = CryptoInputParcel.createCryptoInputParcel(otherPassphrase);
parcel.setNewUnlock(new ChangeUnlockParcel(otherPassphrase)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(otherPassphrase));
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, modified = applyModificationWithChecks(builder.build(), modified, onlyA, onlyB,
new CryptoInputParcel(new Date(), new Passphrase())); CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()));
Assert.assertEquals("exactly three packets should have been modified (the secret keys)", Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
3, onlyB.size()); 3, onlyB.size());
{ // quick check to make sure no two secret keys have the same IV { // quick check to make sure no two secret keys have the same IV
HashSet<ByteBuffer> ivs = new HashSet<ByteBuffer>(); HashSet<ByteBuffer> ivs = new HashSet<>();
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
SecretKeyPacket p = (SecretKeyPacket) new BCPGInputStream( SecretKeyPacket p = (SecretKeyPacket) new BCPGInputStream(
new ByteArrayInputStream(onlyB.get(i).buf)).readPacket(); new ByteArrayInputStream(onlyB.get(i).buf)).readPacket();
@@ -1185,7 +1179,7 @@ public class PgpKeyOperationTest {
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true); Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true);
parcel.setNewUnlock(new ChangeUnlockParcel(otherPassphrase2)); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(otherPassphrase2));
{ {
// if we replace a secret key with one without passphrase // if we replace a secret key with one without passphrase
modified = KeyringTestingHelper.removePacket(modified, sKeyNoPassphrase.position); modified = KeyringTestingHelper.removePacket(modified, sKeyNoPassphrase.position);
@@ -1194,7 +1188,7 @@ public class PgpKeyOperationTest {
// we should still be able to modify it (and change its passphrase) without errors // we should still be able to modify it (and change its passphrase) without errors
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, otherCryptoInput, parcel); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, otherCryptoInput, builder.build());
Assert.assertTrue("key modification must succeed", result.success()); Assert.assertTrue("key modification must succeed", result.success());
Assert.assertFalse("log must not contain a warning", Assert.assertFalse("log must not contain a warning",
result.getLog().containsWarnings()); result.getLog().containsWarnings());
@@ -1210,7 +1204,8 @@ public class PgpKeyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(otherPassphrase2), parcel); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(otherPassphrase2), builder.build());
Assert.assertTrue("key modification must succeed", result.success()); Assert.assertTrue("key modification must succeed", result.success());
Assert.assertTrue("log must contain a failed passphrase change warning", Assert.assertTrue("log must contain a failed passphrase change warning",
result.getLog().containsType(LogType.MSG_MF_PASSPHRASE_FAIL)); result.getLog().containsType(LogType.MSG_MF_PASSPHRASE_FAIL));
@@ -1223,9 +1218,10 @@ public class PgpKeyOperationTest {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0);
parcel.mAddUserIds.add("discord"); builder.addUserId("discord");
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date()), parcel); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date()), builder.build());
Assert.assertFalse("non-restricted operations should fail without passphrase", result.success()); Assert.assertFalse("non-restricted operations should fail without passphrase", result.success());
} }
@@ -1296,8 +1292,8 @@ public class PgpKeyOperationTest {
CanonicalizedKeyRing canonicalized = inputKeyRing.canonicalize(new OperationLog(), 0); CanonicalizedKeyRing canonicalized = inputKeyRing.canonicalize(new OperationLog(), 0);
Assert.assertNotNull("canonicalization must succeed", canonicalized); Assert.assertNotNull("canonicalization must succeed", canonicalized);
ArrayList onlyA = new ArrayList<RawPacket>(); ArrayList onlyA = new ArrayList<>();
ArrayList onlyB = new ArrayList<RawPacket>(); ArrayList onlyB = new ArrayList<>();
//noinspection unchecked //noinspection unchecked
Assert.assertTrue("keyrings differ", !KeyringTestingHelper.diffKeyrings( Assert.assertTrue("keyrings differ", !KeyringTestingHelper.diffKeyrings(
expectedKeyRing.getEncoded(), expectedKeyRing.getEncoded(), onlyA, onlyB)); expectedKeyRing.getEncoded(), expectedKeyRing.getEncoded(), onlyA, onlyB));
@@ -59,6 +59,7 @@ import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@@ -95,27 +96,27 @@ public class UncachedKeyringCanonicalizeTest {
Security.insertProviderAt(new BouncyCastleProvider(), 1); Security.insertProviderAt(new BouncyCastleProvider(), 1);
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); builder.addUserId("twi");
parcel.mAddUserIds.add("pink"); builder.addUserId("pink");
{ {
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(100, WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(100,
"sunshine, sunshine, ladybugs awake~".getBytes()); "sunshine, sunshine, ladybugs awake~".getBytes());
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());
staticRing = result.getRing(); staticRing = result.getRing();
Assert.assertNotNull("initial test key creation must succeed", staticRing); Assert.assertNotNull("initial test key creation must succeed", staticRing);
@@ -351,14 +352,14 @@ public class UncachedKeyringCanonicalizeTest {
@Test public void testForeignSignature() throws Exception { @Test public void testForeignSignature() throws Exception {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("trix"); builder.addUserId("trix");
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
UncachedKeyRing foreign = op.createSecretKeyRing(parcel).getRing(); UncachedKeyRing foreign = op.createSecretKeyRing(builder.build()).getRing();
Assert.assertNotNull("initial test key creation must succeed", foreign); Assert.assertNotNull("initial test key creation must succeed", foreign);
PGPSecretKey foreignSecretKey = PGPSecretKey foreignSecretKey =
@@ -549,7 +550,7 @@ public class UncachedKeyringCanonicalizeTest {
CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey(); CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey();
masterSecretKey.unlock(new Passphrase()); masterSecretKey.unlock(new Passphrase());
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date()); CryptoInputParcel cryptoInput = CryptoInputParcel.createCryptoInputParcel(new Date());
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature( PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput), PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
cryptoInput.getSignatureTime(), cryptoInput.getSignatureTime(),
@@ -18,6 +18,14 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import java.io.ByteArrayInputStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.PacketTags; import org.bouncycastle.bcpg.PacketTags;
import org.bouncycastle.bcpg.S2K; import org.bouncycastle.bcpg.S2K;
@@ -40,18 +48,13 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayInputStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
/** Tests for the UncachedKeyring.merge method. /** Tests for the UncachedKeyring.merge method.
* *
@@ -86,7 +89,7 @@ public class UncachedKeyringMergeTest {
ArrayList<RawPacket> onlyB = new ArrayList<>(); ArrayList<RawPacket> onlyB = new ArrayList<>();
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
PgpKeyOperation op; PgpKeyOperation op;
SaveKeyringParcel parcel; SaveKeyringParcel.Builder builder;
@BeforeClass @BeforeClass
public static void setUpOnce() throws Exception { public static void setUpOnce() throws Exception {
@@ -94,43 +97,42 @@ public class UncachedKeyringMergeTest {
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("twi"); builder.addUserId("twi");
parcel.mAddUserIds.add("pink"); builder.addUserId("pink");
{ {
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(100, WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(100,
"sunshine, sunshine, ladybugs awake~".getBytes()); "sunshine, sunshine, ladybugs awake~".getBytes());
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
staticRingA = result.getRing(); staticRingA = result.getRing();
staticRingA = staticRingA.canonicalize(new OperationLog(), 0).getUncachedKeyRing(); staticRingA = staticRingA.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
} }
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); builder.addUserId("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog(); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
staticRingB = result.getRing(); staticRingB = result.getRing();
staticRingB = staticRingB.canonicalize(new OperationLog(), 0).getUncachedKeyRing(); staticRingB = staticRingB.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
} }
@@ -152,10 +154,11 @@ public class UncachedKeyringMergeTest {
// setting up some parameters just to reduce code duplication // setting up some parameters just to reduce code duplication
op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100)); op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
// set this up, gonna need it more than once resetBuilder();
parcel = new SaveKeyringParcel(); }
parcel.mMasterKeyId = ringA.getMasterKeyId();
parcel.mFingerprint = ringA.getFingerprint(); private void resetBuilder() {
builder = SaveKeyringParcel.buildChangeKeyringParcel(ringA.getMasterKeyId(), ringA.getFingerprint());
} }
public void testSelfNoOp() throws Exception { public void testSelfNoOp() throws Exception {
@@ -187,13 +190,15 @@ public class UncachedKeyringMergeTest {
CanonicalizedSecretKeyRing secretRing = CanonicalizedSecretKeyRing secretRing =
new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0); new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0);
parcel.reset(); resetBuilder();
parcel.mAddUserIds.add("flim"); builder.addUserId("flim");
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); modifiedA = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
parcel.reset(); resetBuilder();
parcel.mAddUserIds.add("flam"); builder.addUserId("flam");
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); modifiedB = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
} }
{ // merge A into base { // merge A into base
@@ -227,11 +232,13 @@ public class UncachedKeyringMergeTest {
{ {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0);
parcel.reset(); resetBuilder();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); modifiedA = op.modifySecretKeyRing(secretRing,
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
modifiedB = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2); subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2); subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
@@ -268,11 +275,12 @@ public class UncachedKeyringMergeTest {
public void testAddedKeySignature() throws Exception { public void testAddedKeySignature() throws Exception {
final UncachedKeyRing modified; { final UncachedKeyRing modified; {
parcel.reset(); resetBuilder();
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1)); builder.addRevokeSubkey(KeyringTestingHelper.getSubkeyId(ringA, 1));
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), 0); ringA.getEncoded(), 0);
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); modified = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
} }
{ {
@@ -299,7 +307,8 @@ public class UncachedKeyringMergeTest {
ringB.getEncoded(), 0).getSecretKey(); ringB.getEncoded(), 0).getSecretKey();
secretKey.unlock(new Passphrase()); secretKey.unlock(new Passphrase());
PgpCertifyOperation op = new PgpCertifyOperation(); PgpCertifyOperation op = new PgpCertifyOperation();
CertifyAction action = new CertifyAction(pubRing.getMasterKeyId(), publicRing.getPublicKey().getUnorderedUserIds(), null); CertifyAction action = CertifyAction.createForUserIds(
pubRing.getMasterKeyId(), publicRing.getPublicKey().getUnorderedUserIds());
// sign all user ids // sign all user ids
PgpCertifyResult result = op.certify(secretKey, publicRing, new OperationLog(), 0, action, null, new Date()); PgpCertifyResult result = op.certify(secretKey, publicRing, new OperationLog(), 0, action, null, new Date());
Assert.assertTrue("certification must succeed", result.success()); Assert.assertTrue("certification must succeed", result.success());
@@ -359,7 +368,7 @@ public class UncachedKeyringMergeTest {
public void testAddedUserAttributeSignature() throws Exception { public void testAddedUserAttributeSignature() throws Exception {
final UncachedKeyRing modified; { final UncachedKeyRing modified; {
parcel.reset(); resetBuilder();
Random r = new Random(); Random r = new Random();
int type = r.nextInt(110)+1; int type = r.nextInt(110)+1;
@@ -367,11 +376,12 @@ public class UncachedKeyringMergeTest {
new Random().nextBytes(data); new Random().nextBytes(data);
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data); WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data);
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), 0); ringA.getEncoded(), 0);
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing(); modified = op.modifySecretKeyRing(secretRing,
CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing();
} }
{ {
@@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -53,16 +54,16 @@ public class UncachedKeyringTest {
Security.insertProviderAt(new BouncyCastleProvider(), 1); Security.insertProviderAt(new BouncyCastleProvider(), 1);
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L)); Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); builder.addUserId("twi");
parcel.mAddUserIds.add("pink"); builder.addUserId("pink");
{ {
Random r = new Random(); Random r = new Random();
int type = r.nextInt(110)+1; int type = r.nextInt(110)+1;
@@ -70,13 +71,13 @@ public class UncachedKeyringTest {
new Random().nextBytes(data); new Random().nextBytes(data);
WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data); WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data);
parcel.mAddUserAttribute.add(uat); builder.addUserAttribute(uat);
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase())); builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase()));
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
staticRing = result.getRing(); staticRing = result.getRing();
staticPubRing = staticRing.extractPublicKeyRing(); staticPubRing = staticRing.extractPublicKeyRing();
@@ -143,8 +143,8 @@ public class InteropTest {
Passphrase pass = new Passphrase(config.getString("passphrase")); Passphrase pass = new Passphrase(config.getString("passphrase"));
PgpDecryptVerifyOperation op = makeOperation(base.toString(), pass, decrypt, verify); PgpDecryptVerifyOperation op = makeOperation(base.toString(), pass, decrypt, verify);
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(); PgpDecryptVerifyInputParcel input = PgpDecryptVerifyInputParcel.builder().build();
CryptoInputParcel cip = new CryptoInputParcel(pass); CryptoInputParcel cip = CryptoInputParcel.createCryptoInputParcel(pass);
DecryptVerifyResult result = op.execute(input, cip, data, out); DecryptVerifyResult result = op.execute(input, cip, data, out);
byte[] plaintext = config.getString("textcontent").getBytes("utf-8"); byte[] plaintext = config.getString("textcontent").getBytes("utf-8");
String filename = config.getString("filename"); String filename = config.getString("filename");
@@ -194,11 +194,12 @@ public class KeychainExternalProviderTest {
} }
private void certifyKey(long secretMasterKeyId, long publicMasterKeyId, String userId) { private void certifyKey(long secretMasterKeyId, long publicMasterKeyId, String userId) {
CertifyActionsParcel certifyActionsParcel = new CertifyActionsParcel(secretMasterKeyId); CertifyActionsParcel.Builder certifyActionsParcel = CertifyActionsParcel.builder(secretMasterKeyId);
certifyActionsParcel.add(new CertifyAction(publicMasterKeyId, Collections.singletonList(userId), null)); certifyActionsParcel.addAction(
CertifyAction.createForUserIds(publicMasterKeyId, Collections.singletonList(userId)));
CertifyOperation op = new CertifyOperation( CertifyOperation op = new CertifyOperation(
RuntimeEnvironment.application, databaseInteractor, new ProgressScaler(), null); RuntimeEnvironment.application, databaseInteractor, new ProgressScaler(), null);
CertifyResult certifyResult = op.execute(certifyActionsParcel, new CryptoInputParcel()); CertifyResult certifyResult = op.execute(certifyActionsParcel.build(), CryptoInputParcel.createCryptoInputParcel());
assertTrue(certifyResult.success()); assertTrue(certifyResult.success());
} }
+2
View File
@@ -16,6 +16,8 @@ buildscript {
// allows to check for lib updates with "./gradlew dependencyUpdates -Drevision=release" // allows to check for lib updates with "./gradlew dependencyUpdates -Drevision=release"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
} }
} }
+3
View File
@@ -12,3 +12,6 @@ platform:
# see https://github.com/mikepenz/Android-Iconics/blob/develop/LICENSE # see https://github.com/mikepenz/Android-Iconics/blob/develop/LICENSE
tests: tests:
unlicensed: skip unlicensed: skip
'com.ryanharter.auto.value:auto-value-parcel-adapter':
tests:
unlicensed: skip
@@ -1,5 +1,6 @@
/** /*
* Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
* Copyright (c) 2017 Vincent Breitmoser
* *
* Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
*/ */
@@ -8,6 +9,8 @@ package org.bouncycastle.openpgp.operator.jcajce;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper; import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -19,25 +22,27 @@ import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
{ {
private final PublicKeyDataDecryptorFactory mWrappedDecryptor; private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
private final Map<ByteBuffer, byte[]> mSessionKeyCache; private final HashMap<ByteBuffer, byte[]> mSessionKeyCache;
private OperatorHelper mOperatorHelper; private OperatorHelper mOperatorHelper;
public CachingDataDecryptorFactory(String providerName, public CachingDataDecryptorFactory(String providerName, Map<ByteBuffer, byte[]> sessionKeyCache)
final Map<ByteBuffer,byte[]> sessionKeyCache)
{ {
mWrappedDecryptor = null; this((PublicKeyDataDecryptorFactory) null, sessionKeyCache);
mSessionKeyCache = sessionKeyCache;
mOperatorHelper = new OperatorHelper(new NamedJcaJceHelper(providerName)); mOperatorHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
} }
public CachingDataDecryptorFactory(PublicKeyDataDecryptorFactory wrapped, public CachingDataDecryptorFactory(PublicKeyDataDecryptorFactory wrapped,
final Map<ByteBuffer,byte[]> sessionKeyCache) Map<ByteBuffer, byte[]> sessionKeyCache)
{ {
mWrappedDecryptor = wrapped; mSessionKeyCache = new HashMap<>();
mSessionKeyCache = sessionKeyCache; if (sessionKeyCache != null)
{
mSessionKeyCache.putAll(sessionKeyCache);
}
mWrappedDecryptor = wrapped;
} }
public boolean hasCachedSessionData(PGPPublicKeyEncryptedData encData) throws PGPException { public boolean hasCachedSessionData(PGPPublicKeyEncryptedData encData) throws PGPException {
@@ -46,7 +51,7 @@ public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactor
} }
public Map<ByteBuffer, byte[]> getCachedSessionKeys() { public Map<ByteBuffer, byte[]> getCachedSessionKeys() {
return mSessionKeyCache; return Collections.unmodifiableMap(mSessionKeyCache);
} }
public boolean canDecrypt() { public boolean canDecrypt() {