Allow for hidden recipients in encrypt methods

This commit is contained in:
Dominik Schürmann 2015-03-16 18:20:44 +01:00
parent a5271bf229
commit fd8f5ebb2f
13 changed files with 134 additions and 34 deletions

View File

@ -55,8 +55,8 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
return new IterableIterator<String>(mPublicKey.getUserIDs()); return new IterableIterator<String>(mPublicKey.getUserIDs());
} }
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() { JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator(boolean hiddenRecipients) {
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey); return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey, hiddenRecipients);
} }
public boolean canSign() { public boolean canSign() {

View File

@ -65,9 +65,6 @@ public abstract class KeyRing {
* <p/> * <p/>
* User ID matching: * User ID matching:
* http://fiddle.re/t4p6f * http://fiddle.re/t4p6f
*
* @param userId
* @return theParsedUserInfo
*/ */
public static UserId splitUserId(final String userId) { public static UserId splitUserId(final String userId) {
if (!TextUtils.isEmpty(userId)) { if (!TextUtils.isEmpty(userId)) {
@ -81,11 +78,6 @@ public abstract class KeyRing {
/** /**
* Returns a composed user id. Returns null if name is null! * Returns a composed user id. Returns null if name is null!
*
* @param name
* @param email
* @param comment
* @return
*/ */
public static String createUserId(UserId userId) { public static String createUserId(UserId userId) {
String userIdString = userId.name; // consider name a required value String userIdString = userId.name; // consider name a required value

View File

@ -1,3 +1,21 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* 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; package org.sufficientlysecure.keychain.pgp;
public interface PassphraseCacheInterface { public interface PassphraseCacheInterface {

View File

@ -1,3 +1,21 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* 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; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;
@ -23,7 +41,8 @@ public class PgpSignEncryptInput {
protected boolean mFailOnMissingEncryptionKeyIds = false; protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset; protected String mCharset;
protected boolean mCleartextSignature; protected boolean mCleartextSignature;
protected boolean mDetachedSignature; protected boolean mDetachedSignature = false;
protected boolean mHiddenRecipients = false;
public String getCharset() { public String getCharset() {
return mCharset; return mCharset;
@ -33,7 +52,7 @@ public class PgpSignEncryptInput {
this.mCharset = mCharset; this.mCharset = mCharset;
} }
public boolean ismFailOnMissingEncryptionKeyIds() { public boolean isFailOnMissingEncryptionKeyIds() {
return mFailOnMissingEncryptionKeyIds; return mFailOnMissingEncryptionKeyIds;
} }
@ -126,7 +145,7 @@ public class PgpSignEncryptInput {
return this; return this;
} }
public boolean ismEnableAsciiArmorOutput() { public boolean isEnableAsciiArmorOutput() {
return mEnableAsciiArmorOutput; return mEnableAsciiArmorOutput;
} }
@ -172,5 +191,14 @@ public class PgpSignEncryptInput {
public boolean isDetachedSignature() { public boolean isDetachedSignature() {
return mDetachedSignature; return mDetachedSignature;
} }
public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) {
this.mHiddenRecipients = hiddenRecipients;
return this;
}
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org> * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* *
@ -60,7 +60,6 @@ import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean; 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.
@ -117,7 +116,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
Log.d(Constants.TAG, "enableSignature:" + enableSignature Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption + "\nenableEncryption:" + enableEncryption
+ "\nenableCompression:" + enableCompression + "\nenableCompression:" + enableCompression
+ "\nenableAsciiArmorOutput:" + input.ismEnableAsciiArmorOutput()); + "\nenableAsciiArmorOutput:" + input.isEnableAsciiArmorOutput()
+ "\nisHiddenRecipients:" + input.isHiddenRecipients());
// add additional key id to encryption ids (mostly to do self-encryption) // add additional key id to encryption ids (mostly to do self-encryption)
if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) { if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) {
@ -127,7 +127,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
ArmoredOutputStream armorOut = null; ArmoredOutputStream armorOut = null;
OutputStream out; OutputStream out;
if (input.ismEnableAsciiArmorOutput()) { if (input.isEnableAsciiArmorOutput()) {
armorOut = new ArmoredOutputStream(outputStream); armorOut = new ArmoredOutputStream(outputStream);
if (input.getVersionHeader() != null) { if (input.getVersionHeader() != null) {
armorOut.setHeader("Version", input.getVersionHeader()); armorOut.setHeader("Version", input.getVersionHeader());
@ -254,19 +254,19 @@ public class PgpSignEncryptOperation extends BaseOperation {
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing( CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id)); KeyRings.buildUnifiedKeyRingUri(id));
CanonicalizedPublicKey key = keyRing.getEncryptionSubKey(); CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator()); cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1, log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id)); KeyFormattingUtils.convertKeyIdToHex(id));
} catch (PgpKeyNotFoundException e) { } catch (PgpKeyNotFoundException e) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1, log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id)); KeyFormattingUtils.convertKeyIdToHex(id));
if (input.ismFailOnMissingEncryptionKeyIds()) { if (input.isFailOnMissingEncryptionKeyIds()) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1, log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id)); KeyFormattingUtils.convertKeyIdToHex(id));
if (input.ismFailOnMissingEncryptionKeyIds()) { if (input.isFailOnMissingEncryptionKeyIds()) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
} }
@ -280,7 +280,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
updateProgress(R.string.progress_preparing_signature, 4, 100); updateProgress(R.string.progress_preparing_signature, 4, 100);
try { try {
boolean cleartext = input.isCleartextSignature() && input.ismEnableAsciiArmorOutput() && !enableEncryption; boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getSignatureGenerator( signatureGenerator = signingKey.getSignatureGenerator(
input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp()); input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
@ -358,7 +358,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
literalGen.close(); literalGen.close();
indent -= 1; indent -= 1;
} else if (enableSignature && input.isCleartextSignature() && input.ismEnableAsciiArmorOutput()) { } else if (enableSignature && input.isCleartextSignature() && input.isEnableAsciiArmorOutput()) {
/* cleartext signature: sign-only of ascii text */ /* cleartext signature: sign-only of ascii text */
updateProgress(R.string.progress_signing, 8, 100); updateProgress(R.string.progress_signing, 8, 100);
@ -404,7 +404,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
// handle output stream separately for detached signatures // handle output stream separately for detached signatures
detachedByteOut = new ByteArrayOutputStream(); detachedByteOut = new ByteArrayOutputStream();
OutputStream detachedOut = detachedByteOut; OutputStream detachedOut = detachedByteOut;
if (input.ismEnableAsciiArmorOutput()) { if (input.isEnableAsciiArmorOutput()) {
detachedArmorOut = new ArmoredOutputStream(detachedOut); detachedArmorOut = new ArmoredOutputStream(detachedOut);
if (input.getVersionHeader() != null) { if (input.getVersionHeader() != null) {
detachedArmorOut.setHeader("Version", input.getVersionHeader()); detachedArmorOut.setHeader("Version", input.getVersionHeader());

View File

@ -1,3 +1,21 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* 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; package org.sufficientlysecure.keychain.pgp;
import android.net.Uri; import android.net.Uri;
@ -52,6 +70,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
mCharset = src.readString(); mCharset = src.readString();
mCleartextSignature = src.readInt() == 1; mCleartextSignature = src.readInt() == 1;
mDetachedSignature = src.readInt() == 1; mDetachedSignature = src.readInt() == 1;
mHiddenRecipients = src.readInt() == 1;
mInputUris = src.createTypedArrayList(Uri.CREATOR); mInputUris = src.createTypedArrayList(Uri.CREATOR);
mOutputUris = src.createTypedArrayList(Uri.CREATOR); mOutputUris = src.createTypedArrayList(Uri.CREATOR);
@ -116,6 +135,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
dest.writeString(mCharset); dest.writeString(mCharset);
dest.writeInt(mCleartextSignature ? 1 : 0); dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0); dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
dest.writeTypedList(mInputUris); dest.writeTypedList(mInputUris);
dest.writeTypedList(mOutputUris); dest.writeTypedList(mOutputUris);

View File

@ -30,6 +30,7 @@ public interface EncryptActivityInterface {
public boolean isUseArmor(); public boolean isUseArmor();
public boolean isUseCompression(); public boolean isUseCompression();
public boolean isEncryptFilenames(); public boolean isEncryptFilenames();
public boolean isHiddenRecipients();
public long getSignatureKey(); public long getSignatureKey();
public long[] getEncryptionKeys(); public long[] getEncryptionKeys();

View File

@ -62,15 +62,18 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
private static final int MODE_SYMMETRIC = 1; private static final int MODE_SYMMETRIC = 1;
// model used by fragments // model used by fragments
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
private String mPassphrase = "";
private boolean mUseArmor = false; private boolean mUseArmor = false;
private boolean mUseCompression = true; private boolean mUseCompression = true;
private boolean mDeleteAfterEncrypt = false; private boolean mDeleteAfterEncrypt = false;
private boolean mShareAfterEncrypt = false; private boolean mShareAfterEncrypt = false;
private boolean mEncryptFilenames = true; private boolean mEncryptFilenames = true;
private boolean mHiddenRecipients = false;
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
private String mPassphrase = "";
private ArrayList<Uri> mInputUris; private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris; private ArrayList<Uri> mOutputUris;
private String mMessage = ""; private String mMessage = "";
@ -94,6 +97,11 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
return mEncryptFilenames; return mEncryptFilenames;
} }
@Override
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
@Override @Override
public long getSignatureKey() { public long getSignatureKey() {
return mSigningKeyId; return mSigningKeyId;
@ -228,6 +236,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
} else { } else {
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
} }
data.setHiddenRecipients(mHiddenRecipients);
data.setEnableAsciiArmorOutput(mUseArmor); data.setEnableAsciiArmorOutput(mUseArmor);
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
@ -377,12 +386,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
notifyUpdate(); notifyUpdate();
break; break;
} }
case R.id.encrypt_filenames: { case R.id.check_encrypt_filenames: {
mEncryptFilenames = item.isChecked(); mEncryptFilenames = item.isChecked();
notifyUpdate(); notifyUpdate();
break; break;
} }
case R.id.check_hidden_recipients: {
mHiddenRecipients = item.isChecked();
notifyUpdate();
break;
}
default: { default: {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@ -62,16 +62,19 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
private static final int MODE_SYMMETRIC = 1; private static final int MODE_SYMMETRIC = 1;
// model used by fragments // model used by fragments
private boolean mShareAfterEncrypt = false;
private boolean mUseCompression = true;
private boolean mHiddenRecipients = false;
private long mEncryptionKeyIds[] = null; private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null; private String mEncryptionUserIds[] = null;
// TODO Constants.key.none? What's wrong with a null value? // TODO Constants.key.none? What's wrong with a null value?
private long mSigningKeyId = Constants.key.none; private long mSigningKeyId = Constants.key.none;
private String mPassphrase = ""; private String mPassphrase = "";
private boolean mShareAfterEncrypt = false;
private ArrayList<Uri> mInputUris; private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris; private ArrayList<Uri> mOutputUris;
private String mMessage = ""; private String mMessage = "";
private boolean mUseCompression = true;
public boolean isModeSymmetric() { public boolean isModeSymmetric() {
return MODE_SYMMETRIC == mCurrentMode; return MODE_SYMMETRIC == mCurrentMode;
@ -92,6 +95,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
return mUseCompression; return mUseCompression;
} }
@Override
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
@Override @Override
public long getSignatureKey() { public long getSignatureKey() {
return mSigningKeyId; return mSigningKeyId;
@ -206,6 +214,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
} else { } else {
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
} }
data.setHiddenRecipients(mHiddenRecipients);
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
@ -357,6 +366,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
notifyUpdate(); notifyUpdate();
break; break;
} }
case R.id.check_hidden_recipients: {
mHiddenRecipients = item.isChecked();
notifyUpdate();
break;
}
default: { default: {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@ -31,7 +31,7 @@
android:checkable="true" /> android:checkable="true" />
<item <item
android:id="@+id/encrypt_filenames" android:id="@+id/check_encrypt_filenames"
android:title="@string/label_encrypt_filenames" android:title="@string/label_encrypt_filenames"
android:checked="true" android:checked="true"
android:checkable="true" /> android:checkable="true" />
@ -40,5 +40,11 @@
android:id="@+id/check_use_armor" android:id="@+id/check_use_armor"
android:title="@string/label_file_ascii_armor" android:title="@string/label_file_ascii_armor"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/check_hidden_recipients"
android:title="@string/label_hidden_recipients"
android:checked="false"
android:checkable="true" />
</menu> </menu>

View File

@ -24,4 +24,11 @@
android:title="@string/label_enable_compression" android:title="@string/label_enable_compression"
android:checked="true" android:checked="true"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/check_hidden_recipients"
android:title="@string/label_hidden_recipients"
android:checked="false"
android:checkable="true" />
</menu> </menu>

View File

@ -168,6 +168,7 @@
<string name="label_preferred">"preferred"</string> <string name="label_preferred">"preferred"</string>
<string name="label_enable_compression">"Enable compression"</string> <string name="label_enable_compression">"Enable compression"</string>
<string name="label_encrypt_filenames">"Encrypt filenames"</string> <string name="label_encrypt_filenames">"Encrypt filenames"</string>
<string name="label_hidden_recipients">"Hide recipients"</string>
<string name="user_id_no_name">"&lt;no name&gt;"</string> <string name="user_id_no_name">"&lt;no name&gt;"</string>
<string name="none">"&lt;none&gt;"</string> <string name="none">"&lt;none&gt;"</string>

2
extern/spongycastle vendored

@ -1 +1 @@
Subproject commit 939914d9ffd1e8cc2710de6c600c9ccfc86aa545 Subproject commit 4bb0180faa920f4e8cf3d482976a34e4df982a8d