added a "force V3 signature" setting similar to the GPG version, hopefully making APG useful for some special cases where that is needed

Update issue 39
Two new strings:
<string name="section_advanced">Advanced</string>
<string name="label_forceV3Signature">Force V3 Signatures</string>

"V3" is just "version 3" and should remain untranslated, both strings can be seen at the bottom of the preferences activity.
This commit is contained in:
Thialfihar 2010-07-27 22:41:50 +00:00
parent 20f7755b2c
commit b13eb7dbf3
7 changed files with 151 additions and 35 deletions

View File

@ -46,6 +46,7 @@
<string name="section_keys">Keys</string> <string name="section_keys">Keys</string>
<string name="section_general">General</string> <string name="section_general">General</string>
<string name="section_defaults">Defaults</string> <string name="section_defaults">Defaults</string>
<string name="section_advanced">Advanced</string>
<!-- btn_lowerCase: capitalized words, no punctuation --> <!-- btn_lowerCase: capitalized words, no punctuation -->
<string name="btn_signToClipboard">Sign To Clipboard</string> <string name="btn_signToClipboard">Sign To Clipboard</string>
@ -106,6 +107,7 @@
<string name="label_messageCompression">Message Compression</string> <string name="label_messageCompression">Message Compression</string>
<string name="label_fileCompression">File Compression</string> <string name="label_fileCompression">File Compression</string>
<string name="label_language">Language</string> <string name="label_language">Language</string>
<string name="label_forceV3Signature">Force V3 Signatures</string>
<string name="noKeysSelected">Select</string> <string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string> <string name="oneKeySelected">1 Selected</string>

View File

@ -66,4 +66,14 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:title="@string/section_advanced">
<CheckBoxPreference
android:persistent="false"
android:key="forceV3Signatures"
android:title="@string/label_forceV3Signature" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -78,6 +78,7 @@ import org.bouncycastle2.openpgp.PGPSignatureList;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle2.openpgp.PGPUtil; import org.bouncycastle2.openpgp.PGPUtil;
import org.bouncycastle2.openpgp.PGPV3SignatureGenerator;
import org.thialfihar.android.apg.provider.DataProvider; import org.thialfihar.android.apg.provider.DataProvider;
import org.thialfihar.android.apg.provider.Database; import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.provider.KeyRings; import org.thialfihar.android.apg.provider.KeyRings;
@ -1123,6 +1124,7 @@ public class Apg {
String signaturePassPhrase, String signaturePassPhrase,
ProgressDialogUpdater progress, ProgressDialogUpdater progress,
int symmetricAlgorithm, int hashAlgorithm, int compression, int symmetricAlgorithm, int hashAlgorithm, int compression,
boolean forceV3Signature,
String passPhrase) String passPhrase)
throws IOException, GeneralException, PGPException, NoSuchProviderException, throws IOException, GeneralException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException { NoSuchAlgorithmException, SignatureException {
@ -1165,7 +1167,6 @@ public class Apg {
new BouncyCastleProvider()); new BouncyCastleProvider());
} }
PGPSignatureGenerator signatureGenerator = null;
progress.setProgress(R.string.progress_preparingStreams, 5, 100); progress.setProgress(R.string.progress_preparingStreams, 5, 100);
// encrypt and compress input file content // encrypt and compress input file content
PGPEncryptedDataGenerator cPk = PGPEncryptedDataGenerator cPk =
@ -1184,18 +1185,29 @@ public class Apg {
} }
encryptOut = cPk.open(out, new byte[1 << 16]); encryptOut = cPk.open(out, new byte[1 << 16]);
PGPSignatureGenerator signatureGenerator = null;
PGPV3SignatureGenerator signatureV3Generator = null;
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
progress.setProgress(R.string.progress_preparingSignature, 10, 100); progress.setProgress(R.string.progress_preparingSignature, 10, 100);
signatureGenerator = if (forceV3Signature) {
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(), signatureV3Generator =
hashAlgorithm, new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
new BouncyCastleProvider()); hashAlgorithm,
signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); new BouncyCastleProvider());
String userId = getMainUserId(getMasterKey(signingKeyRing)); signatureV3Generator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
} else {
signatureGenerator =
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); String userId = getMainUserId(getMasterKey(signingKeyRing));
spGen.setSignerUserID(false, userId); PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
signatureGenerator.setHashedSubpackets(spGen.generate()); spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
}
} }
PGPCompressedDataGenerator compressGen = null; PGPCompressedDataGenerator compressGen = null;
@ -1207,7 +1219,11 @@ public class Apg {
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
} }
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); if (forceV3Signature) {
signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
} else {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
}
} }
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
@ -1223,7 +1239,11 @@ public class Apg {
while ((n = in.read(buffer)) > 0) { while ((n = in.read(buffer)) > 0) {
pOut.write(buffer, 0, n); pOut.write(buffer, 0, n);
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
signatureGenerator.update(buffer, 0, n); if (forceV3Signature) {
signatureV3Generator.update(buffer, 0, n);
} else {
signatureGenerator.update(buffer, 0, n);
}
} }
done += n; done += n;
if (data.getSize() != 0) { if (data.getSize() != 0) {
@ -1235,7 +1255,11 @@ public class Apg {
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
progress.setProgress(R.string.progress_generatingSignature, 95, 100); progress.setProgress(R.string.progress_generatingSignature, 95, 100);
signatureGenerator.generate().encode(pOut); if (forceV3Signature) {
signatureV3Generator.generate().encode(pOut);
} else {
signatureGenerator.generate().encode(pOut);
}
} }
if (compressGen != null) { if (compressGen != null) {
compressGen.close(); compressGen.close();
@ -1252,6 +1276,7 @@ public class Apg {
InputData data, OutputStream outStream, InputData data, OutputStream outStream,
long signatureKeyId, String signaturePassPhrase, long signatureKeyId, String signaturePassPhrase,
int hashAlgorithm, int hashAlgorithm,
boolean forceV3Signature,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws GeneralException, PGPException, IOException, NoSuchAlgorithmException, throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
SignatureException { SignatureException {
@ -1281,20 +1306,30 @@ public class Apg {
signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(), signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
PGPSignatureGenerator signatureGenerator = null;
progress.setProgress(R.string.progress_preparingStreams, 0, 100); progress.setProgress(R.string.progress_preparingStreams, 0, 100);
progress.setProgress(R.string.progress_preparingSignature, 30, 100); progress.setProgress(R.string.progress_preparingSignature, 30, 100);
signatureGenerator =
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
String userId = getMainUserId(getMasterKey(signingKeyRing));
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); PGPSignatureGenerator signatureGenerator = null;
spGen.setSignerUserID(false, userId); PGPV3SignatureGenerator signatureV3Generator = null;
signatureGenerator.setHashedSubpackets(spGen.generate()); if (forceV3Signature) {
signatureV3Generator =
new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
hashAlgorithm,
new BouncyCastleProvider());
signatureV3Generator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
} else {
signatureGenerator =
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
String userId = getMainUserId(getMasterKey(signingKeyRing));
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
}
progress.setProgress(R.string.progress_signing, 40, 100); progress.setProgress(R.string.progress_signing, 40, 100);
@ -1304,16 +1339,29 @@ public class Apg {
InputStream inStream = data.getInputStream(); InputStream inStream = data.getInputStream();
int lookAhead = readInputLine(lineOut, inStream); int lookAhead = readInputLine(lineOut, inStream);
processLine(armorOut, signatureGenerator, lineOut.toByteArray()); if (forceV3Signature) {
processLine(armorOut, signatureV3Generator, lineOut.toByteArray());
} else {
processLine(armorOut, signatureGenerator, lineOut.toByteArray());
}
if (lookAhead != -1) { if (lookAhead != -1) {
do { do {
lookAhead = readInputLine(lineOut, lookAhead, inStream); lookAhead = readInputLine(lineOut, lookAhead, inStream);
signatureGenerator.update((byte)'\r'); if (forceV3Signature) {
signatureGenerator.update((byte)'\n'); signatureV3Generator.update((byte)'\r');
signatureV3Generator.update((byte)'\n');
} else {
signatureGenerator.update((byte)'\r');
signatureGenerator.update((byte)'\n');
}
processLine(armorOut, signatureGenerator, lineOut.toByteArray()); if (forceV3Signature) {
processLine(armorOut, signatureV3Generator, lineOut.toByteArray());
} else {
processLine(armorOut, signatureGenerator, lineOut.toByteArray());
}
} }
while (lookAhead != -1); while (lookAhead != -1);
} }
@ -1321,7 +1369,11 @@ public class Apg {
armorOut.endClearText(); armorOut.endClearText();
BCPGOutputStream bOut = new BCPGOutputStream(armorOut); BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
signatureGenerator.generate().encode(bOut); if (forceV3Signature) {
signatureV3Generator.generate().encode(bOut);
} else {
signatureGenerator.generate().encode(bOut);
}
armorOut.close(); armorOut.close();
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
@ -1802,6 +1854,16 @@ public class Apg {
aOut.write(line, 0, line.length); aOut.write(line, 0, line.length);
} }
private static void processLine(OutputStream aOut, PGPV3SignatureGenerator sGen, byte[] line)
throws SignatureException, IOException {
int length = getLengthWithoutWhiteSpace(line);
if (length > 0) {
sGen.update(line, 0, length);
}
aOut.write(line, 0, line.length);
}
private static int getLengthWithoutSeparator(byte[] line) { private static int getLengthWithoutSeparator(byte[] line) {
int end = line.length - 1; int end = line.length - 1;

View File

@ -33,5 +33,6 @@ public final class Constants {
public static final String default_file_compression = "defaultFileCompression"; public static final String default_file_compression = "defaultFileCompression";
public static final String pass_phrase_cache_ttl = "passPhraseCacheTtl"; public static final String pass_phrase_cache_ttl = "passPhraseCacheTtl";
public static final String language = "language"; public static final String language = "language";
public static final String force_v3_signatures = "forceV3Signatures";
} }
} }

View File

@ -619,16 +619,15 @@ public class EncryptActivity extends BaseActivity {
String error = null; String error = null;
Bundle data = new Bundle(); Bundle data = new Bundle();
Message msg = new Message(); Message msg = new Message();
fillDataSource();
fillDataDestination();
try { try {
InputData in; InputData in;
OutputStream out; OutputStream out;
boolean useAsciiArmour = true; boolean useAsciiArmour = true;
long encryptionKeyIds[] = null; long encryptionKeyIds[] = null;
long signatureKeyId = 0; long signatureKeyId = 0;
boolean signOnly = false;
int compressionId = 0; int compressionId = 0;
boolean signOnly = false;
String passPhrase = null; String passPhrase = null;
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
@ -642,6 +641,9 @@ public class EncryptActivity extends BaseActivity {
signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
} }
fillDataSource(signOnly && !mReturnResult);
fillDataDestination();
// streams // streams
in = mDataSource.getInputData(this, true); in = mDataSource.getInputData(this, true);
out = mDataDestination.getOutputStream(this); out = mDataDestination.getOutputStream(this);
@ -661,14 +663,18 @@ public class EncryptActivity extends BaseActivity {
if (signOnly) { if (signOnly) {
Apg.signText(this, in, out, getSecretKeyId(), Apg.signText(this, in, out, getSecretKeyId(),
Apg.getCachedPassPhrase(getSecretKeyId()), Apg.getCachedPassPhrase(getSecretKeyId()),
mPreferences.getDefaultHashAlgorithm(), this); mPreferences.getDefaultHashAlgorithm(),
mPreferences.getForceV3Signatures(),
this);
} else { } else {
Apg.encrypt(this, in, out, useAsciiArmour, Apg.encrypt(this, in, out, useAsciiArmour,
encryptionKeyIds, signatureKeyId, encryptionKeyIds, signatureKeyId,
Apg.getCachedPassPhrase(signatureKeyId), this, Apg.getCachedPassPhrase(signatureKeyId), this,
mPreferences.getDefaultEncryptionAlgorithm(), mPreferences.getDefaultEncryptionAlgorithm(),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getDefaultHashAlgorithm(),
compressionId, passPhrase); compressionId,
mPreferences.getForceV3Signatures(),
passPhrase);
} }
out.close(); out.close();
@ -930,7 +936,7 @@ public class EncryptActivity extends BaseActivity {
return super.onCreateDialog(id); return super.onCreateDialog(id);
} }
protected void fillDataSource() { protected void fillDataSource(boolean fixContent) {
mDataSource = new DataSource(); mDataSource = new DataSource();
if (mContentUri != null) { if (mContentUri != null) {
mDataSource.setUri(mContentUri); mDataSource.setUri(mContentUri);
@ -940,7 +946,19 @@ public class EncryptActivity extends BaseActivity {
if (mData != null) { if (mData != null) {
mDataSource.setData(mData); mDataSource.setData(mData);
} else { } else {
mDataSource.setText(mMessage.getText().toString()); String message = mMessage.getText().toString();
if (fixContent) {
// fix the message a bit, trailing spaces and newlines break stuff,
// because GMail sends as HTML and such things fuck up the
// signature,
// TODO: things like "<" and ">" also fuck up the signature
message = message.replaceAll(" +\n", "\n");
message = message.replaceAll("\n\n+", "\n\n");
message = message.replaceFirst("^\n+", "");
// make sure there'll be exactly one newline at the end
message = message.replaceFirst("\n*$", "\n");
}
mDataSource.setText(message);
} }
} }
} }

View File

@ -103,6 +103,16 @@ public class Preferences {
editor.commit(); editor.commit();
} }
public boolean getForceV3Signatures() {
return mSharedPreferences.getBoolean(Constants.pref.force_v3_signatures, false);
}
public void setForceV3Signatures(boolean value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putBoolean(Constants.pref.force_v3_signatures, value);
editor.commit();
}
public boolean hasSeenChangeLog(String version) { public boolean hasSeenChangeLog(String version) {
return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version, return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version,
false); false);

View File

@ -37,6 +37,7 @@ public class PreferencesActivity extends PreferenceActivity {
private IntegerListPreference mMessageCompression = null; private IntegerListPreference mMessageCompression = null;
private IntegerListPreference mFileCompression = null; private IntegerListPreference mFileCompression = null;
private CheckBoxPreference mAsciiArmour = null; private CheckBoxPreference mAsciiArmour = null;
private CheckBoxPreference mForceV3Signatures = null;
private Preferences mPreferences; private Preferences mPreferences;
@Override @Override
@ -210,6 +211,18 @@ public class PreferencesActivity extends PreferenceActivity {
return false; return false;
} }
}); });
mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.force_v3_signatures);
mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
mForceV3Signatures.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
public boolean onPreferenceChange(Preference preference, Object newValue)
{
mForceV3Signatures.setChecked((Boolean)newValue);
mPreferences.setForceV3Signatures((Boolean)newValue);
return false;
}
});
} }
} }