mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 03:02:15 -05:00
backend support for charset in ascii-armored streams
This commit is contained in:
parent
efe5c80b1c
commit
6c80025ead
@ -438,6 +438,60 @@ public class PgpEncryptDecryptTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForeignEncoding () throws Exception {
|
||||
String plaintext = "ウィキペディア";
|
||||
byte[] plaindata = plaintext.getBytes("iso-2022-jp");
|
||||
|
||||
{ // some quick sanity checks
|
||||
Assert.assertEquals(plaintext, new String(plaindata, "iso-2022-jp"));
|
||||
Assert.assertNotEquals(plaintext, new String(plaindata, "utf-8"));
|
||||
}
|
||||
|
||||
byte[] ciphertext;
|
||||
{ // encrypt data with a given passphrase
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(plaindata);
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
Builder b = new PgpSignEncrypt.Builder(
|
||||
Robolectric.application,
|
||||
new ProviderHelper(Robolectric.application),
|
||||
null, // new DummyPassphraseCache(mPassphrase, 0L),
|
||||
data, out);
|
||||
|
||||
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||
// this only works with ascii armored output!
|
||||
b.setEnableAsciiArmorOutput(true);
|
||||
b.setCharset("iso-2022-jp");
|
||||
SignEncryptResult result = b.build().execute();
|
||||
Assert.assertTrue("encryption must succeed", result.success());
|
||||
|
||||
ciphertext = out.toByteArray();
|
||||
}
|
||||
|
||||
{ // decryption with provided passphrase should yield the same result
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||
InputData data = new InputData(in, in.available());
|
||||
|
||||
PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, null, null, null);
|
||||
b.setPassphrase(mKeyPhrase1);
|
||||
DecryptVerifyResult result = b.build().execute();
|
||||
Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
|
||||
Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes",
|
||||
out.toByteArray(), plaindata);
|
||||
Assert.assertEquals("charset should be read correctly",
|
||||
"iso-2022-jp", result.getCharset());
|
||||
Assert.assertEquals("decrypted ciphertext should equal plaintext",
|
||||
new String(out.toByteArray(), result.getCharset()), plaintext);
|
||||
Assert.assertNull("signature be empty", result.getSignatureResult());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
|
||||
InputData data, OutputStream out,
|
||||
final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
|
||||
|
@ -41,6 +41,9 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
|
||||
OpenPgpSignatureResult mSignatureResult;
|
||||
OpenPgpMetadata mDecryptMetadata;
|
||||
// This holds the charset which was specified in the ascii armor, if specified
|
||||
// https://tools.ietf.org/html/rfc4880#page56
|
||||
String mCharset;
|
||||
|
||||
public long getKeyIdPassphraseNeeded() {
|
||||
return mKeyIdPassphraseNeeded;
|
||||
@ -84,6 +87,14 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
mDecryptMetadata = decryptMetadata;
|
||||
}
|
||||
|
||||
public String getCharset () {
|
||||
return mCharset;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
mCharset = charset;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return (mResult & RESULT_PENDING) == RESULT_PENDING;
|
||||
}
|
||||
|
@ -564,6 +564,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DC_ASKIP_NO_KEY (LogLevel.DEBUG, R.string.msg_dc_askip_no_key),
|
||||
MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed),
|
||||
MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
|
||||
MSG_DC_CHARSET (LogLevel.DEBUG, R.string.msg_dc_charset),
|
||||
MSG_DC_CLEAR_DATA (LogLevel.DEBUG, R.string.msg_dc_clear_data),
|
||||
MSG_DC_CLEAR_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
|
||||
MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file),
|
||||
|
@ -234,6 +234,22 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
boolean symmetricPacketFound = false;
|
||||
boolean anyPacketFound = false;
|
||||
|
||||
// If the input stream is armored, and there is a charset specified, take a note for later
|
||||
// https://tools.ietf.org/html/rfc4880#page56
|
||||
String charset = null;
|
||||
if (in instanceof ArmoredInputStream) {
|
||||
for (String header : ((ArmoredInputStream) in).getArmorHeaders()) {
|
||||
String[] pieces = header.split(":", 2);
|
||||
if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) {
|
||||
charset = pieces[1].trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (charset != null) {
|
||||
log.add(LogType.MSG_DC_CHARSET, indent, charset);
|
||||
}
|
||||
}
|
||||
|
||||
// go through all objects and find one we can decrypt
|
||||
while (it.hasNext()) {
|
||||
Object obj = it.next();
|
||||
@ -550,6 +566,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
log.add(LogType.MSG_DC_OK_META_ONLY, indent);
|
||||
DecryptVerifyResult result =
|
||||
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||
result.setCharset(charset);
|
||||
result.setDecryptMetadata(metadata);
|
||||
return result;
|
||||
}
|
||||
@ -647,6 +664,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||
result.setDecryptMetadata(metadata);
|
||||
result.setSignatureResult(signatureResultBuilder.build());
|
||||
result.setCharset(charset);
|
||||
return result;
|
||||
|
||||
}
|
||||
@ -807,7 +825,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
while ((ch = fIn.read()) >= 0) {
|
||||
bOut.write(ch);
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
lookAhead = readPassedEOL(bOut, ch, fIn);
|
||||
lookAhead = readPastEOL(bOut, ch, fIn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -824,7 +842,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
do {
|
||||
bOut.write(ch);
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
lookAhead = readPassedEOL(bOut, ch, fIn);
|
||||
lookAhead = readPastEOL(bOut, ch, fIn);
|
||||
break;
|
||||
}
|
||||
} while ((ch = fIn.read()) >= 0);
|
||||
@ -836,7 +854,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
return lookAhead;
|
||||
}
|
||||
|
||||
private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
|
||||
private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
|
||||
throws IOException {
|
||||
int lookAhead = fIn.read();
|
||||
|
||||
|
@ -81,6 +81,7 @@ public class PgpSignEncrypt extends BaseOperation {
|
||||
private boolean mCleartextInput;
|
||||
private String mOriginalFilename;
|
||||
private boolean mFailOnMissingEncryptionKeyIds;
|
||||
private String mCharset;
|
||||
|
||||
private byte[] mNfcSignedHash = null;
|
||||
private Date mNfcCreationTimestamp = null;
|
||||
@ -118,6 +119,7 @@ public class PgpSignEncrypt extends BaseOperation {
|
||||
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
|
||||
this.mOriginalFilename = builder.mOriginalFilename;
|
||||
this.mFailOnMissingEncryptionKeyIds = builder.mFailOnMissingEncryptionKeyIds;
|
||||
this.mCharset = builder.mCharset;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@ -145,6 +147,7 @@ public class PgpSignEncrypt extends BaseOperation {
|
||||
private byte[] mNfcSignedHash = null;
|
||||
private Date mNfcCreationTimestamp = null;
|
||||
private boolean mFailOnMissingEncryptionKeyIds = false;
|
||||
private String mCharset = null;
|
||||
|
||||
public Builder(Context context, ProviderHelper providerHelper, Progressable progressable,
|
||||
InputData data, OutputStream outStream) {
|
||||
@ -211,6 +214,11 @@ public class PgpSignEncrypt extends BaseOperation {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCharset(String charset) {
|
||||
mCharset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Also encrypt with the signing keyring
|
||||
*
|
||||
@ -283,6 +291,10 @@ public class PgpSignEncrypt extends BaseOperation {
|
||||
if (mVersionHeader != null) {
|
||||
armorOut.setHeader("Version", mVersionHeader);
|
||||
}
|
||||
// if we have a charset, put it in the header
|
||||
if (mCharset != null) {
|
||||
armorOut.setHeader("Charset", mCharset);
|
||||
}
|
||||
out = armorOut;
|
||||
} else {
|
||||
out = mOutStream;
|
||||
|
@ -557,6 +557,12 @@ public class OpenPgpService extends RemoteService {
|
||||
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
String charset = pgpResult.getCharset();
|
||||
if (charset != null) {
|
||||
result.putExtra(OpenPgpApi.RESULT_CHARSET, charset);
|
||||
}
|
||||
|
||||
} else {
|
||||
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
|
||||
throw new Exception(getString(errorMsg.mType.getMsgId()));
|
||||
|
@ -41,6 +41,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ShareHelper;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class DecryptTextFragment extends DecryptFragment {
|
||||
public static final String ARG_CIPHERTEXT = "ciphertext";
|
||||
|
||||
@ -194,7 +196,18 @@ public class DecryptTextFragment extends DecryptFragment {
|
||||
|
||||
byte[] decryptedMessage = returnData
|
||||
.getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
|
||||
mText.setText(new String(decryptedMessage));
|
||||
String displayMessage;
|
||||
if (pgpResult.getCharset() != null) {
|
||||
try {
|
||||
displayMessage = new String(decryptedMessage, pgpResult.getCharset());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// if we can't decode properly, just fall back to utf-8
|
||||
displayMessage = new String(decryptedMessage);
|
||||
}
|
||||
} else {
|
||||
displayMessage = new String(decryptedMessage);
|
||||
}
|
||||
mText.setText(displayMessage);
|
||||
|
||||
pgpResult.createNotify(getActivity()).show();
|
||||
|
||||
|
@ -923,6 +923,7 @@
|
||||
<string name="msg_dc_askip_no_key">"Data not encrypted with known key, skipping…"</string>
|
||||
<string name="msg_dc_askip_not_allowed">"Data not encrypted with allowed key, skipping…"</string>
|
||||
<string name="msg_dc_asym">"Found block of asymmetrically encrypted data for key %s"</string>
|
||||
<string name="msg_dc_charset">"Found charset header: '%s'"</string>
|
||||
<string name="msg_dc_clear_data">"Processing literal data"</string>
|
||||
<string name="msg_dc_clear_decompress">"Unpacking compressed data"</string>
|
||||
<string name="msg_dc_clear_meta_file">"Filename: %s"</string>
|
||||
|
Loading…
Reference in New Issue
Block a user