Merge remote-tracking branch 'origin/master' into encrypted-export

This commit is contained in:
Vincent Breitmoser
2015-09-28 18:27:29 +02:00
71 changed files with 2111 additions and 756 deletions

View File

@@ -263,9 +263,7 @@
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT_DATA" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
</intent-filter>
<!-- DECRYPT_TEXT -->

View File

@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGmzCCBIOgAwIBAgIJAPzhpcIBaOeNMA0GCSqGSIb3DQEBBQUAMIGPMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMRQwEgYDVQQK
EwtLZXliYXNlIExMQzEXMBUGA1UECxMOQ2VydCBBdXRob3JpdHkxEzARBgNVBAMT
CmtleWJhc2UuaW8xHDAaBgkqhkiG9w0BCQEWDWNhQGtleWJhc2UuaW8wHhcNMTQw
MTAyMTY0MjMzWhcNMjMxMjMxMTY0MjMzWjCBjzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgTAk5ZMREwDwYDVQQHEwhOZXcgWW9yazEUMBIGA1UEChMLS2V5YmFzZSBMTEMx
FzAVBgNVBAsTDkNlcnQgQXV0aG9yaXR5MRMwEQYDVQQDEwprZXliYXNlLmlvMRww
GgYJKoZIhvcNAQkBFg1jYUBrZXliYXNlLmlvMIICIjANBgkqhkiG9w0BAQEFAAOC
Ag8AMIICCgKCAgEA3sLA6ZG8uOvmlFvFLVIOURmcQrZyMFKbVu9/TeDiemls3w3/
JzVTduD+7KiUi9R7QcCW/V1ZpReTfunm7rfACiJ1fpIkjSQrgsvKDLghIzxIS5FM
I8utet5p6QtuJhaAwmmXn8xX05FvqWNbrcXRdpL4goFdigPsFK2xhTUiWatLMste
oShI7+zmrgkx75LeLMD0bL2uOf87JjOzbY8x2sUIZLGwPoATyG8WS38ey6KkJxRj
AhG3p+OTYEjYSrsAtQA6ImbeDpfSHKOB8HF3nVp//Eb4HEiEsWwBRbQXvAWh3DYL
GukFW0wiO0HVCoWY+bHL/Mqa0NdRGOlLsbL4Z4pLrhqKgSDU8umX9YuNRRaB0P5n
TkzyU6axHqzq990Gep/I62bjsBdYYp+DjSPK43mXRrfWJl2NTcl8xKAyfsOW+9hQ
9vwK0tpSicNxfYuUZs0BhfjSZ/Tc6Z1ERdgUYRiXTtohl+SRA2IgZMloHCllVMNj
EjXhguvHgLAOrcuyhVBupiUQGUHQvkMsr1Uz8VPNDFOJedwucRU2AaR881bknnSb
ds9+zNLsvUFV+BK7Qdnt/WkFpYL78rGwY47msi9Ooddx6fPyeg3qkJGM6cwn/boy
w9lQeleYDq8kyJdixIAxtAskNzRPJ4nDu2izTfByQoM8epwAWboc/gNFObMCAwEA
AaOB9zCB9DAdBgNVHQ4EFgQURqpATOw1gVVrzlqqFKbkfaKXvwowgcQGA1UdIwSB
vDCBuYAURqpATOw1gVVrzlqqFKbkfaKXvwqhgZWkgZIwgY8xCzAJBgNVBAYTAlVT
MQswCQYDVQQIEwJOWTERMA8GA1UEBxMITmV3IFlvcmsxFDASBgNVBAoTC0tleWJh
c2UgTExDMRcwFQYDVQQLEw5DZXJ0IEF1dGhvcml0eTETMBEGA1UEAxMKa2V5YmFz
ZS5pbzEcMBoGCSqGSIb3DQEJARYNY2FAa2V5YmFzZS5pb4IJAPzhpcIBaOeNMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBAA3Z5FIhulYghMuHdcHYTYWc
7xT5WD4hXQ0WALZs4p5Y+b2Af54o6v1wUE1Au97FORq5CsFXX/kGl/JzzTimeucn
YJwGuXMpilrlHCBAL5/lSQjA7qbYIolQ3SB9ON+LYuF1jKB9k8SqNp7qzucxT3tO
b8ZMDEPNsseC7NE2uwNtcW3yrTh6WZnSqg/jwswiWjHYDdG7U8FjMYlRol3wPux2
PizGbSgiR+ztI2OthxtxNWMrT9XKxNQTpcxOXnLuhiSwqH8PoY17ecP8VPpaa0K6
zym0zSkbroqydazaxcXRk3eSlc02Ktk7HzRzuqQQXhRMkxVnHbFHgGsz03L533pm
mlIEgBMggZkHwNvs1LR7f3v2McdKulDH7Mv8yyfguuQ5Jxxt7RJhUuqSudbEhoaM
6jAJwBkMFxsV2YnyFEd3eZ/qBYPf7TYHhyzmHW6WkSypGqSnXd4gYpJ8o7LxSf4F
inLjxRD+H9Xn1UVXWLM0gaBB7zZcXd2zjMpRsWgezf5IR5vyakJsc7fxzgor3Qeq
Ri6LvdEkhhFVl5rHMQBwNOPngySrq8cs/ikTLTfQVTYXXA4Ba1YyiMOlfaR1LhKw
If1AkUV0tfCTNRZ01EotKSK77+o+k214n+BAu+7mO+9B5Kb7lMFQcuWCHXKYB2Md
cT7Yh09F0QpFUd0ymEfv
-----END CERTIFICATE-----

View File

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFpzCCBI+gAwIBAgIQSCQjuTbnogvWCWWHeCDMbzANBgkqhkiG9w0BAQsFADB2
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCTUkxEjAQBgNVBAcTCUFubiBBcmJvcjES
MBAGA1UEChMJSW50ZXJuZXQyMREwDwYDVQQLEwhJbkNvbW1vbjEfMB0GA1UEAxMW
SW5Db21tb24gUlNBIFNlcnZlciBDQTAeFw0xNDEwMDkwMDAwMDBaFw0xNzEwMDgy
MzU5NTlaMIHlMQswCQYDVQQGEwJVUzEOMAwGA1UEERMFMDIxMzkxCzAJBgNVBAgT
Ak1hMRIwEAYDVQQHEwlDYW1icmlkZ2UxHTAbBgNVBAkTFDc3IE1hc3NhY2h1c2V0
dHMgQXZlMS4wLAYDVQQKEyVNYXNzYWNodXNldHRzIEluc3RpdHV0ZSBvZiBUZWNo
bm9sb2d5MSowKAYDVQQLFCFJbmZvcm1hdGlvbiBTZXJ2aWNlcyAmIFRlY2hub2xv
Z3kxFDASBgNVBAsTC1BsYXRpbnVtU1NMMRQwEgYDVQQDEwtwZ3AubWl0LmVkdTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOXCQWXwK1O/saHfUEJjeE6w
VvTMe8xgl5qmkU+9U2TS6HdyVItD9fHZ3sAwVHo7mYtLGXp0S8F2hiiyLgQeQo84
F/owinPaPU8c+2Ogw464HbROmjU7Vc/iHQklA0kR+lZsFwZuWd+nYjmPrNfm87Ik
k9Wenco7wwFUquoJ8XZW1RVTr9WRWWlyNKwPnil5aBUGtbG6CP1+IFN75xfJYjz5
g+JcLHYsKyb6JhPYxT42ZdgTPKVRJNuIpyOMXMIPB/qFgUyU+2T/g7vxoa3THllq
vkp/ds5lpDe+uu6H9mbtMYvX5w9TBqt7YPegWcTUhGERnytXxeNpncYkzGMMUN0C
AwEAAaOCAb8wggG7MB8GA1UdIwQYMBaAFB4Fo3ePbJbiW4dLprSGrHEADOc4MB0G
A1UdDgQWBBRISoMA6cVQE5089wT6LFO4aiNnzTAOBgNVHQ8BAf8EBAMCBaAwDAYD
VR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwZwYDVR0g
BGAwXjBSBgwrBgEEAa4jAQQDAQEwQjBABggrBgEFBQcCARY0aHR0cHM6Ly93d3cu
aW5jb21tb24ub3JnL2NlcnQvcmVwb3NpdG9yeS9jcHNfc3NsLnBkZjAIBgZngQwB
AgIwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDovL2NybC5pbmNvbW1vbi1yc2Eub3Jn
L0luQ29tbW9uUlNBU2VydmVyQ0EuY3JsMHUGCCsGAQUFBwEBBGkwZzA+BggrBgEF
BQcwAoYyaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0luQ29tbW9uUlNBU2VydmVy
Q0FfMi5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20w
FgYDVR0RBA8wDYILcGdwLm1pdC5lZHUwDQYJKoZIhvcNAQELBQADggEBAHbQqv2o
LrRD8rMzaHvPHVa92gfi6bpEsiRsVw3kpH4D4k+PL9LWkgtgTWpM+MvskiUvS9ay
FbWdXiy/peOj421fwnL/re9gmWs1g7FtUrDgIpz2T2jonPqbnIJPMHxI+ICWZMYH
V/dO844geRKAiGs/UZbG4Uf1Jo0PxtPtD5puaUk4l9Va8WHU2OLq0kzS9K+iu/sx
z0XG+fAMneyiXm5jtfjYE2W8/h61RhZulSUmYBkiMLzKr5eqe2VIkMqyTfyZ5zms
1LZ1GWaouMsTBN1+2TXssQ71L1tIZg/lXJVlfVRkwOIV5Mp3ohxLSBZT8qNSef1v
mFNa+DGU1sdl6m4=
-----END CERTIFICATE-----

View File

@@ -91,14 +91,16 @@ public class KeychainApplication extends Application {
}
brandGlowEffect(getApplicationContext(),
FormattingUtils.getColorFromAttr(getApplicationContext(), R.attr.colorPrimary));
FormattingUtils.getColorFromAttr(getApplicationContext(), R.attr.colorPrimary));
setupAccountAsNeeded(this);
// Update keyserver list as needed
Preferences.getPreferences(this).upgradePreferences(this);
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
TlsHelper.addPinnedCertificate("hkps.pool.sks-keyservers.net", getAssets(), "hkps.pool.sks-keyservers.net.CA.cer");
TlsHelper.addPinnedCertificate("pgp.mit.edu", getAssets(), "pgp.mit.edu.cer");
TlsHelper.addPinnedCertificate("api.keybase.io", getAssets(), "api.keybase.io.CA.cer");
TemporaryStorageProvider.cleanUp(this);

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) Andreas Jakl
*
* 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.experimental;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* The BitInputStream allows reading individual bits from a
* general Java InputStream.
* Like the various Stream-classes from Java, the BitInputStream
* has to be created based on another Input stream. It provides
* a function to read the next bit from the sream, as well as to read multiple
* bits at once and write the resulting data into an integer value.
* <p/>
* source: http://developer.nokia.com/Community/Wiki/Bit_Input/Output_Stream_utility_classes_for_efficient_data_transfer
*/
public class BitInputStream {
/**
* The Java InputStream this class is working on.
*/
private InputStream iIs;
/**
* The buffer containing the currently processed
* byte of the input stream.
*/
private int iBuffer;
/**
* Next bit of the current byte value that the user will
* get. If it's 8, the next bit will be read from the
* next byte of the InputStream.
*/
private int iNextBit = 8;
/**
* Create a new bit input stream based on an existing Java InputStream.
*
* @param aIs the input stream this class should read the bits from.
*/
public BitInputStream(InputStream aIs) {
iIs = aIs;
}
/**
* Read a specified number of bits and return them combined as
* an integer value. The bits are written to the integer
* starting at the highest bit ( << aNumberOfBits ), going down
* to the lowest bit ( << 0 )
*
* @param aNumberOfBits defines how many bits to read from the stream.
* @return integer value containing the bits read from the stream.
* @throws IOException
*/
synchronized public int readBits(final int aNumberOfBits)
throws IOException {
int value = 0;
for (int i = aNumberOfBits - 1; i >= 0; i--) {
value |= (readBit() << i);
}
return value;
}
synchronized public int available() {
try {
return (8 - iNextBit) + iIs.available() * 8; // bytestream to bitstream available
} catch (Exception e) {
return 0;
}
}
/**
* Read the next bit from the stream.
*
* @return 0 if the bit is 0, 1 if the bit is 1.
* @throws IOException
*/
synchronized public int readBit() throws IOException {
if (iIs == null)
throw new IOException("Already closed");
if (iNextBit == 8) {
iBuffer = iIs.read();
if (iBuffer == -1)
throw new EOFException();
iNextBit = 0;
}
int bit = iBuffer & (1 << iNextBit);
iNextBit++;
bit = (bit == 0) ? 0 : 1;
return bit;
}
/**
* Close the underlying input stream.
*
* @throws IOException
*/
public void close() throws IOException {
iIs.close();
iIs = null;
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Jake McGinty (Open Whisper Systems)
*
* 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.experimental;
import android.content.Context;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* From https://github.com/mcginty/TextSecure/tree/mnemonic-poem
*/
public class SentenceConfirm {
Context context;
List<String> n, vi, vt, adj, adv, p, art;
public SentenceConfirm(Context context) {
this.context = context;
try {
n = readFile(R.raw.fp_sentence_nouns);
vi = readFile(R.raw.fp_sentence_verbs_i);
vt = readFile(R.raw.fp_sentence_verbs_t);
adj = readFile(R.raw.fp_sentence_adjectives);
adv = readFile(R.raw.fp_sentence_adverbs);
p = readFile(R.raw.fp_sentence_prepositions);
art = readFile(R.raw.fp_sentence_articles);
} catch (IOException e) {
Log.e(Constants.TAG, "Reading sentence files failed", e);
}
}
List<String> readFile(int resId) throws IOException {
if (context.getApplicationContext() == null) {
throw new AssertionError("app context can't be null");
}
BufferedReader in = new BufferedReader(new InputStreamReader(
context.getApplicationContext()
.getResources()
.openRawResource(resId)));
List<String> words = new ArrayList<>();
String word = in.readLine();
while (word != null) {
words.add(word);
word = in.readLine();
}
in.close();
return words;
}
public String fromBytes(final byte[] bytes, int desiredBytes) throws IOException {
BitInputStream bin = new BitInputStream(new ByteArrayInputStream(bytes));
EntropyString fingerprint = new EntropyString();
while (fingerprint.getBits() < (desiredBytes * 8)) {
if (!fingerprint.isEmpty()) {
fingerprint.append("\n\n");
}
try {
fingerprint.append(getSentence(bin));
} catch (IOException e) {
Log.e(Constants.TAG, "IOException when creating the sentence");
throw e;
}
}
return fingerprint.toString();
}
/**
* Grab a word for a list of them using the necessary bits to choose from a BitInputStream
*
* @param words the list of words to select from
* @param bin the bit input stream to encode from
* @return A Pair of the word and the number of bits consumed from the stream
*/
private EntropyString getWord(List<String> words, BitInputStream bin) throws IOException {
final int neededBits = log(words.size(), 2);
Log.d(Constants.TAG, "need " + neededBits + " bits of entropy");
int bits = bin.readBits(neededBits);
Log.d(Constants.TAG, "got word " + words.get(bits) + " with " + neededBits + " bits of entropy");
return new EntropyString(words.get(bits), neededBits);
}
private EntropyString getNounPhrase(BitInputStream bits) throws IOException {
final EntropyString phrase = new EntropyString();
phrase.append(getWord(art, bits)).append(" ");
if (bits.readBit() != 0) {
phrase.append(getWord(adj, bits)).append(" ");
}
phrase.incBits();
phrase.append(getWord(n, bits));
Log.d(Constants.TAG, "got phrase " + phrase + " with " + phrase.getBits() + " bits of entropy");
return phrase;
}
EntropyString getSentence(BitInputStream bits) throws IOException {
final EntropyString sentence = new EntropyString();
sentence.append(getNounPhrase(bits)); // Subject
if (bits.readBit() != 0) {
sentence.append(" ").append(getWord(vt, bits)); // Transitive verb
sentence.append(" ").append(getNounPhrase(bits)); // Object of transitive verb
} else {
sentence.append(" ").append(getWord(vi, bits)); // Intransitive verb
}
sentence.incBits();
if (bits.readBit() != 0) {
sentence.append(" ").append(getWord(adv, bits)); // Adverb
}
sentence.incBits();
if (bits.readBit() != 0) {
sentence.append(" ").append(getWord(p, bits)); // Preposition
sentence.append(" ").append(getNounPhrase(bits)); // Object of preposition
}
sentence.incBits();
Log.d(Constants.TAG, "got sentence " + sentence + " with " + sentence.getBits() + " bits of entropy");
// uppercase first character, end with dot (without increasing the bits)
sentence.getBuilder().replace(0, 1,
Character.toString(Character.toUpperCase(sentence.getBuilder().charAt(0))));
sentence.getBuilder().append(".");
return sentence;
}
public static class EntropyString {
private StringBuilder builder;
private int bits;
public EntropyString(String phrase, int bits) {
this.builder = new StringBuilder(phrase);
this.bits = bits;
}
public EntropyString() {
this("", 0);
}
public StringBuilder getBuilder() {
return builder;
}
public boolean isEmpty() {
return builder.length() == 0;
}
public EntropyString append(EntropyString phrase) {
builder.append(phrase);
bits += phrase.getBits();
return this;
}
public EntropyString append(String string) {
builder.append(string);
return this;
}
public int getBits() {
return bits;
}
public void setBits(int bits) {
this.bits = bits;
}
public void incBits() {
bits += 1;
}
@Override
public String toString() {
return builder.toString();
}
}
private static int log(int x, int base) {
return (int) (Math.log(x) / Math.log(base));
}
}

View File

@@ -15,12 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.util;
package org.sufficientlysecure.keychain.experimental;
import android.content.Context;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedReader;
@@ -29,7 +30,7 @@ import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.BitSet;
public class ExperimentalWordConfirm {
public class WordConfirm {
public static String getWords(Context context, byte[] fingerprintBlob) {
ArrayList<String> words = new ArrayList<>();
@@ -37,7 +38,7 @@ public class ExperimentalWordConfirm {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(
context.getAssets().open("word_confirm_list.txt"),
context.getResources().openRawResource(R.raw.fp_word_list),
"UTF-8"
));

View File

@@ -77,7 +77,7 @@ public class CloudSearch {
// kill threads that haven't returned yet
thread.interrupt();
}
} catch (InterruptedException e) {
} catch (InterruptedException ignored) {
}
}

View File

@@ -23,6 +23,7 @@ import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -196,19 +197,23 @@ public class HkpKeyserver extends Keyserver {
/**
* returns a client with pinned certificate if necessary
*
* @param url url to be queried by client
* @param url url to be queried by client
* @param proxy proxy to be used by client
* @return client with a pinned certificate if necesary
* @return client with a pinned certificate if necessary
*/
public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
OkHttpClient client = new OkHttpClient();
try {
TlsHelper.pinCertificateIfNecessary(client, url);
TlsHelper.usePinnedCertificateIfAvailable(client, url);
} catch (TlsHelper.TlsHelperException e) {
Log.w(Constants.TAG, e);
}
// don't follow any redirects
client.setFollowRedirects(false);
client.setFollowSslRedirects(false);
if (proxy != null) {
client.setProxy(proxy);
client.setConnectTimeout(30000, TimeUnit.MILLISECONDS);
@@ -228,7 +233,7 @@ public class HkpKeyserver extends Keyserver {
OkHttpClient client = getClient(url, proxy);
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
String responseBody = response.body().string();// contains body both in case of success or failure
String responseBody = response.body().string(); // contains body both in case of success or failure
if (response.isSuccessful()) {
return responseBody;
@@ -238,17 +243,12 @@ public class HkpKeyserver extends Keyserver {
} catch (IOException e) {
Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
proxy == null?"":" Using proxy " + proxy);
(proxy == null ? "" : " Using proxy " + proxy));
}
}
/**
* Results are sorted by creation date of key!
*
* @param query
* @return
* @throws QueryFailedException
* @throws QueryNeedsRepairException
*/
@Override
public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
@@ -299,30 +299,46 @@ public class HkpKeyserver extends Keyserver {
entry.setQuery(query);
entry.addOrigin(getUrlPrefix() + mHost + ":" + mPort);
int bitSize = Integer.parseInt(matcher.group(3));
entry.setBitStrength(bitSize);
int algorithmId = Integer.decode(matcher.group(2));
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
// group 1 contains the full fingerprint (v4) or the long key id if available
// see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH);
if (fingerprintOrKeyId.length() > 16) {
if (fingerprintOrKeyId.length() == 40) {
entry.setFingerprintHex(fingerprintOrKeyId);
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
- 16, fingerprintOrKeyId.length()));
} else {
} else if (fingerprintOrKeyId.length() == 16) {
// set key id only
entry.setKeyIdHex("0x" + fingerprintOrKeyId);
} else {
Log.e(Constants.TAG, "Wrong length for fingerprint/long key id.");
// skip this key
continue;
}
final long creationDate = Long.parseLong(matcher.group(4));
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
tmpGreg.setTimeInMillis(creationDate * 1000);
entry.setDate(tmpGreg.getTime());
try {
int bitSize = Integer.parseInt(matcher.group(3));
entry.setBitStrength(bitSize);
int algorithmId = Integer.decode(matcher.group(2));
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
entry.setRevoked(matcher.group(6).contains("r"));
entry.setExpired(matcher.group(6).contains("e"));
final long creationDate = Long.parseLong(matcher.group(4));
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
tmpGreg.setTimeInMillis(creationDate * 1000);
entry.setDate(tmpGreg.getTime());
} catch (NumberFormatException e) {
Log.e(Constants.TAG, "Conversation for bit size, algorithm, or creation date failed.", e);
// skip this key
continue;
}
try {
entry.setRevoked(matcher.group(6).contains("r"));
entry.setExpired(matcher.group(6).contains("e"));
} catch (NullPointerException e) {
Log.e(Constants.TAG, "Check for revocation or expiry failed.", e);
// skip this key
continue;
}
ArrayList<String> userIds = new ArrayList<>();
final String uidLines = matcher.group(7);
@@ -340,6 +356,10 @@ public class HkpKeyserver extends Keyserver {
tmp = URLDecoder.decode(tmp, "UTF8");
} catch (UnsupportedEncodingException ignored) {
// will never happen, because "UTF8" is supported
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "User ID encoding broken", e);
// skip this user id
continue;
}
}
userIds.add(tmp);
@@ -363,11 +383,14 @@ public class HkpKeyserver extends Keyserver {
Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
throw new QueryFailedException("not found");
}
if (data == null) {
throw new QueryFailedException("data is null");
}
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
if (matcher.find()) {
return matcher.group(1);
}
return null;
throw new QueryFailedException("data is null");
}
@Override
@@ -418,7 +441,7 @@ public class HkpKeyserver extends Keyserver {
* Tries to find a server responsible for a given domain
*
* @return A responsible Keyserver or null if not found.
* TODO: PHILIP Add proxy functionality
* TODO: Add proxy functionality
*/
public static HkpKeyserver resolve(String domain) {
try {

View File

@@ -19,12 +19,13 @@ package org.sufficientlysecure.keychain.keyimport;
import com.textuality.keybase.lib.KeybaseException;
import com.textuality.keybase.lib.Match;
import com.textuality.keybase.lib.Search;
import com.textuality.keybase.lib.KeybaseQuery;
import com.textuality.keybase.lib.User;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient;
import java.net.Proxy;
import java.util.ArrayList;
@@ -49,7 +50,9 @@ public class KeybaseKeyserver extends Keyserver {
mQuery = query;
try {
Iterable<Match> matches = Search.search(query, proxy);
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(proxy);
Iterable<Match> matches = keybaseQuery.search(query);
for (Match match : matches) {
results.add(makeEntry(match));
}
@@ -101,7 +104,9 @@ public class KeybaseKeyserver extends Keyserver {
@Override
public String get(String id, Proxy proxy) throws QueryFailedException {
try {
return User.keyForUsername(id, proxy);
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(proxy);
return User.keyForUsername(keybaseQuery, id);
} catch (KeybaseException e) {
throw new QueryFailedException(e.getMessage());
}

View File

@@ -62,15 +62,15 @@ public abstract class Keyserver {
* query too short _or_ too many responses
*/
public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException {
private static final long serialVersionUID = 2703768928624654514L;
private static final long serialVersionUID = 2703768928624654518L;
}
public static class AddKeyException extends Exception {
private static final long serialVersionUID = -507574859137295530L;
}
public abstract List<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
QueryNeedsRepairException;
public abstract List<ImportKeysListEntry> search(String query, Proxy proxy)
throws QueryFailedException, QueryNeedsRepairException;
public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException;

View File

@@ -28,6 +28,7 @@ import java.util.ArrayList;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.codec.DecodeMonitor;
@@ -86,6 +87,11 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
DecryptVerifyResult decryptResult = null;
PgpDecryptVerifyInputParcel decryptInput = input.getDecryptInput();
if (!input.getMimeDecode() && decryptInput == null) {
throw new AssertionError("no decryption or mime decoding, this is probably a bug");
}
if (decryptInput != null) {
log.add(LogType.MSG_DATA_OPENPGP, 1);
@@ -109,16 +115,33 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
return new InputDataResult(InputDataResult.RESULT_ERROR, log);
}
// inform the storage provider about the mime type for this uri
if (decryptResult.getDecryptionMetadata() != null) {
TemporaryStorageProvider.setMimeType(mContext, currentInputUri,
decryptResult.getDecryptionMetadata().getMimeType());
}
} else {
currentInputUri = input.getInputUri();
}
// If we aren't supposed to attempt mime decode, we are done here
if (!input.getMimeDecode()) {
if (decryptInput == null) {
throw new AssertionError("no decryption or mime decoding, this is probably a bug");
// don't even attempt if we know the data isn't suitable for mime content, or if we have a filename
boolean skipMimeParsing = false;
if (decryptResult != null && decryptResult.getDecryptionMetadata() != null) {
OpenPgpMetadata metadata = decryptResult.getDecryptionMetadata();
String fileName = metadata.getFilename();
String contentType = metadata.getMimeType();
if (!TextUtils.isEmpty(fileName)
|| contentType != null
&& !contentType.startsWith("multipart/")
&& !contentType.startsWith("text/")
&& !contentType.startsWith("application/")) {
skipMimeParsing = true;
}
}
// If we aren't supposed to attempt mime decode after decryption, we are done here
if (skipMimeParsing || !input.getMimeDecode()) {
log.add(LogType.MSG_DATA_SKIP_MIME, 1);
@@ -309,25 +332,32 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
log.add(LogType.MSG_DATA_MIME, 1);
// open current uri for input
InputStream in = mContext.getContentResolver().openInputStream(currentInputUri);
parser.parse(in);
try {
if (mSignedDataUri != null) {
if (decryptResult != null) {
decryptResult.setSignatureResult(mSignedDataResult.getSignatureResult());
} else {
decryptResult = mSignedDataResult;
}
// the actual content is the signed data now (and will be passed verbatim, if parsing fails)
currentInputUri = mSignedDataUri;
in = mContext.getContentResolver().openInputStream(currentInputUri);
// reset signed data result, to indicate to the parser that it is in the inner part
mSignedDataResult = null;
// open current uri for input
InputStream in = mContext.getContentResolver().openInputStream(currentInputUri);
parser.parse(in);
if (mSignedDataUri != null) {
if (decryptResult != null) {
decryptResult.setSignatureResult(mSignedDataResult.getSignatureResult());
} else {
decryptResult = mSignedDataResult;
}
// the actual content is the signed data now (and will be passed verbatim, if parsing fails)
currentInputUri = mSignedDataUri;
in = mContext.getContentResolver().openInputStream(currentInputUri);
// reset signed data result, to indicate to the parser that it is in the inner part
mSignedDataResult = null;
parser.parse(in);
}
} catch (MimeException e) {
// a mime error likely means that this wasn't mime data, after all
e.printStackTrace();
log.add(LogType.MSG_DATA_MIME_BAD, 2);
}
// if we found data, return success
@@ -363,10 +393,6 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> {
e.printStackTrace();
log.add(LogType.MSG_DATA_ERROR_IO, 2);
return new InputDataResult(InputDataResult.RESULT_ERROR, log);
} catch (MimeException e) {
e.printStackTrace();
log.add(LogType.MSG_DATA_MIME_ERROR, 2);
return new InputDataResult(InputDataResult.RESULT_ERROR, log);
}
}

View File

@@ -20,39 +20,43 @@
package org.sufficientlysecure.keychain.operations;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.support.annotation.NonNull;
import com.textuality.keybase.lib.KeybaseQuery;
import com.textuality.keybase.lib.Proof;
import com.textuality.keybase.lib.prover.Prover;
import de.measite.minidns.Client;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.TXT;
import org.json.JSONObject;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import de.measite.minidns.Client;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.Data;
import de.measite.minidns.record.TXT;
public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
@@ -83,6 +87,9 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
try {
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(proxy);
String keybaseProof = keybaseInput.mKeybaseProof;
Proof proof = new Proof(new JSONObject(keybaseProof));
mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
@@ -95,7 +102,7 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}
if (!prover.fetchProofData(proxy)) {
if (!prover.fetchProofData(keybaseQuery)) {
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
}

View File

@@ -474,6 +474,7 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_UID_BAD (LogLevel.WARN, R.string.msg_kc_uid_bad),
MSG_KC_UID_CERT_DUP (LogLevel.DEBUG, R.string.msg_kc_uid_cert_dup),
MSG_KC_UID_DUP (LogLevel.DEBUG, R.string.msg_kc_uid_dup),
MSG_KC_UID_TOO_MANY (LogLevel.DEBUG, R.string.msg_kc_uid_too_many),
MSG_KC_UID_FOREIGN (LogLevel.DEBUG, R.string.msg_kc_uid_foreign),
MSG_KC_UID_NO_CERT (LogLevel.DEBUG, R.string.msg_kc_uid_no_cert),
MSG_KC_UID_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_uid_revoke_dup),
@@ -832,7 +833,7 @@ public abstract class OperationResult implements Parcelable {
MSG_DATA_DETACHED_NESTED(LogLevel.WARN, R.string.msg_data_detached_nested),
MSG_DATA_DETACHED_TRAILING (LogLevel.WARN, R.string.msg_data_detached_trailing),
MSG_DATA_DETACHED_UNSUPPORTED (LogLevel.WARN, R.string.msg_data_detached_unsupported),
MSG_DATA_MIME_ERROR (LogLevel.ERROR, R.string.msg_data_mime_error),
MSG_DATA_MIME_BAD(LogLevel.INFO, R.string.msg_data_mime_bad),
MSG_DATA_MIME_FILENAME (LogLevel.DEBUG, R.string.msg_data_mime_filename),
MSG_DATA_MIME_LENGTH (LogLevel.DEBUG, R.string.msg_data_mime_length),
MSG_DATA_MIME (LogLevel.DEBUG, R.string.msg_data_mime),

View File

@@ -52,13 +52,13 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -512,8 +512,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
String originalFilename = literalData.getFileName();
// reject filenames with slashes completely (path traversal issue)
if (originalFilename.contains("/")) {
originalFilename = originalFilename.substring(originalFilename.lastIndexOf('/'));
originalFilename = "";
}
String mimeType = null;
if (literalData.getFormat() == PGPLiteralData.TEXT

View File

@@ -456,11 +456,15 @@ public class UncachedKeyRing {
// check for duplicate user ids
if (processedUserIds.contains(userId)) {
log.add(LogType.MSG_KC_UID_DUP,
indent, userId);
log.add(LogType.MSG_KC_UID_DUP, indent, userId);
// strip out the first found user id with this name
modified = PGPPublicKey.removeCertification(modified, rawUserId);
}
if (processedUserIds.size() > 100) {
log.add(LogType.MSG_KC_UID_TOO_MANY, indent, userId);
// strip out the user id
modified = PGPPublicKey.removeCertification(modified, rawUserId);
}
processedUserIds.add(userId);
PGPSignature selfCert = null;

View File

@@ -54,7 +54,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
private static final int DATABASE_VERSION = 12;
private static final int DATABASE_VERSION = 13;
static Boolean apgHack = false;
private Context mContext;
@@ -296,6 +296,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
// the api_accounts fix and the new update keys table
return;
}
case 13:
// do nothing here, just consolidate
}
@@ -306,6 +308,13 @@ public class KeychainDatabase extends SQLiteOpenHelper {
mContext.getApplicationContext().startActivity(consolidateIntent);
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// NOTE: downgrading the database is explicitly not allowed to prevent
// someone from exploiting old bugs to export the database
throw new RuntimeException("Downgrading the database is not allowed!");
}
/** This method tries to import data from a provided database.
*
* The sole assumptions made on this db are that there is a key_rings table

View File

@@ -33,12 +33,14 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.experimental.SentenceConfirm;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.ExperimentalWordConfirm;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
public class CertifyFingerprintFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@@ -46,24 +48,26 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
static final int REQUEST_CERTIFY = 1;
public static final String ARG_DATA_URI = "uri";
public static final String ARG_ENABLE_WORD_CONFIRM = "enable_word_confirm";
public static final String ARG_ENABLE_PHRASES_CONFIRM = "enable_word_confirm";
private TextView mActionYes;
private TextView mFingerprint;
private TextView mIntro;
private TextView mHeader;
private static final int LOADER_ID_UNIFIED = 0;
private Uri mDataUri;
private boolean mEnableWordConfirm;
private boolean mEnablePhrasesConfirm;
/**
* Creates new instance of this fragment
*/
public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enableWordConfirm) {
public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enablePhrasesConfirm) {
CertifyFingerprintFragment frag = new CertifyFingerprintFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
args.putBoolean(ARG_ENABLE_WORD_CONFIRM, enableWordConfirm);
args.putBoolean(ARG_ENABLE_PHRASES_CONFIRM, enablePhrasesConfirm);
frag.setArguments(args);
@@ -75,11 +79,12 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer());
View actionNo = view.findViewById(R.id.certify_fingerprint_button_no);
View actionYes = view.findViewById(R.id.certify_fingerprint_button_yes);
TextView actionNo = (TextView) view.findViewById(R.id.certify_fingerprint_button_no);
mActionYes = (TextView) view.findViewById(R.id.certify_fingerprint_button_yes);
mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint);
mIntro = (TextView) view.findViewById(R.id.certify_fingerprint_intro);
mHeader = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint_header);
actionNo.setOnClickListener(new View.OnClickListener() {
@Override
@@ -87,7 +92,7 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
getActivity().finish();
}
});
actionYes.setOnClickListener(new View.OnClickListener() {
mActionYes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
certify(mDataUri);
@@ -107,10 +112,12 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
getActivity().finish();
return;
}
mEnableWordConfirm = getArguments().getBoolean(ARG_ENABLE_WORD_CONFIRM);
mEnablePhrasesConfirm = getArguments().getBoolean(ARG_ENABLE_PHRASES_CONFIRM);
if (mEnableWordConfirm) {
mIntro.setText(R.string.certify_fingerprint_text_words);
if (mEnablePhrasesConfirm) {
mIntro.setText(R.string.certify_fingerprint_text_phrases);
mHeader.setText(R.string.section_phrases);
mActionYes.setText(R.string.btn_match_phrases);
}
loadData(dataUri);
@@ -160,7 +167,7 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
if (data.moveToFirst()) {
byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
if (mEnableWordConfirm) {
if (mEnablePhrasesConfirm) {
displayWordConfirm(fingerprintBlob);
} else {
displayHexConfirm(fingerprintBlob);
@@ -180,9 +187,16 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
}
private void displayWordConfirm(byte[] fingerprintBlob) {
String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob);
// String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob);
mFingerprint.setTextSize(24);
String fingerprint;
try {
fingerprint = new SentenceConfirm(getActivity()).fromBytes(fingerprintBlob, 16);
} catch (IOException ioe) {
fingerprint = "-";
}
mFingerprint.setTextSize(18);
mFingerprint.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
mFingerprint.setText(fingerprint);
}

View File

@@ -168,7 +168,7 @@ public class DecryptActivity extends BaseActivity {
return;
}
uris.add(intent.getData());
uris.add(uri);
}
}

View File

@@ -257,7 +257,6 @@ public class DecryptListFragment
}
OpenPgpMetadata metadata = result.mMetadata.get(index);
Uri saveUri = Uri.fromFile(activity.getExternalFilesDir(metadata.getMimeType()));
mCurrentSaveFileUri = result.getOutputUris().get(index);
String filename = metadata.getFilename();
@@ -266,8 +265,8 @@ public class DecryptListFragment
filename = "decrypted" + (ext != null ? "."+ext : "");
}
FileHelper.saveDocument(this, filename, saveUri, metadata.getMimeType(),
R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to, REQUEST_CODE_OUTPUT);
FileHelper.saveDocument(this, filename, metadata.getMimeType(),
REQUEST_CODE_OUTPUT);
}
private void saveFile(Uri saveUri) {
@@ -376,10 +375,12 @@ public class DecryptListFragment
// noinspection deprecation, this should be called from Context, but not available in minSdk
icon = getResources().getDrawable(R.drawable.ic_chat_black_24dp);
} else if (ClipDescription.compareMimeTypes(type, "image/*")) {
int px = FormattingUtils.dpToPx(context, 48);
int px = FormattingUtils.dpToPx(context, 32);
Bitmap bitmap = FileHelper.getThumbnail(context, outputUri, new Point(px, px));
icon = new BitmapDrawable(context.getResources(), bitmap);
} else {
}
if (icon == null) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(outputUri, type);
@@ -445,6 +446,7 @@ public class DecryptListFragment
displayWithViewIntent(result, index, true, true);
break;
case R.id.decrypt_save:
// only inside the menu xml for Android >= 4.4
saveFileDialog(result, index);
break;
}

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
@@ -224,9 +225,8 @@ public class EncryptFilesFragment
String targetName =
(mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), model.inputUri))
+ (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
Uri inputUri = model.inputUri;
FileHelper.saveDocument(this, targetName, inputUri,
R.string.title_encrypt_to_file, R.string.specify_file_to_encrypt_to, REQUEST_CODE_OUTPUT);
FileHelper.saveDocument(this, targetName,
REQUEST_CODE_OUTPUT);
}
public void addFile(Intent data) {
@@ -308,6 +308,17 @@ public class EncryptFilesFragment
return true;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
// Show save only on Android >= 4.4 (Document Provider)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
MenuItem save = menu.findItem(R.id.encrypt_save);
save.setVisible(false);
}
}
public void toggleUseArmor(MenuItem item, final boolean useArmor) {
mUseArmor = useArmor;
@@ -441,9 +452,29 @@ public class EncryptFilesFragment
}
// prepares mOutputUris, either directly and returns false, or indirectly
// which returns true and will call cryptoOperation after mOutputUris has
// been set at a later point.
/**
* Checks that the input uris are not linked to our own internal storage.
* This prevents the encryption of our own database (-> export of whole database)
*/
private void securityCheckInternalStorage() {
for (FilesAdapter.ViewModel model : mFilesAdapter.mDataset) {
File fileInput = new File(model.inputUri.getPath());
try {
// the canonical path of the file must not start with /data/data/org.sufficientlysecure.keychain/
if (fileInput.getCanonicalPath().startsWith(getActivity().getApplicationInfo().dataDir)) {
throw new RuntimeException("Encrypting OpenKeychain's private files is not allowed!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Getting canonical path failed!", e);
}
}
}
/**
* Prepares mOutputUris, either directly and returns false, or indirectly
* which returns true and will call cryptoOperation after mOutputUris has
* been set at a later point.
*/
private boolean prepareOutputStreams() {
switch (mAfterEncryptAction) {
@@ -519,6 +550,8 @@ public class EncryptFilesFragment
}
securityCheckInternalStorage();
return actionsParcel;
}

View File

@@ -155,7 +155,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_VERIFIED);
if (verified) {
Notify.create(getActivity(),
R.string.add_keyserver_verified, Notify.Style.OK).show();
R.string.add_keyserver_connection_verified, Notify.Style.OK).show();
} else {
Notify.create(getActivity(),
R.string.add_keyserver_without_verification,
@@ -177,26 +177,6 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
}
break;
}
case AddEditKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
AddEditKeyserverDialogFragment.FailureReason failureReason =
(AddEditKeyserverDialogFragment.FailureReason) data.getSerializable(
AddEditKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
switch (failureReason) {
case CONNECTION_FAILED: {
Notify.create(getActivity(),
R.string.add_keyserver_connection_failed,
Notify.Style.ERROR).show();
break;
}
case INVALID_URL: {
Notify.create(getActivity(),
R.string.add_keyserver_invalid_url,
Notify.Style.ERROR).show();
break;
}
}
break;
}
}
}
};

View File

@@ -107,7 +107,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
View vFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
View vKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
View vKeySafeButton = view.findViewById(R.id.view_key_action_key_export);
View vKeySaveButton = view.findViewById(R.id.view_key_action_key_export);
View vKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc);
View vKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
ImageButton vKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
@@ -133,7 +133,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
share(false, false);
}
});
vKeySafeButton.setOnClickListener(new View.OnClickListener() {
// Show save only on Android >= 4.4 (Document Provider)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
vKeySaveButton.setVisibility(View.GONE);
}
vKeySaveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
exportToFile();

View File

@@ -40,6 +40,7 @@ import android.widget.TableRow;
import android.widget.TextView;
import com.textuality.keybase.lib.KeybaseException;
import com.textuality.keybase.lib.KeybaseQuery;
import com.textuality.keybase.lib.Proof;
import com.textuality.keybase.lib.User;
@@ -51,6 +52,7 @@ import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
@@ -224,8 +226,9 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
}
}
// look for evidence from keybase in the background, make tabular version of result
//
/**
* look for evidence from keybase in the background, make tabular version of result
*/
private class DescribeKey extends AsyncTask<String, Void, ResultPage> {
ParcelableProxy mParcelableProxy;
@@ -240,7 +243,9 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>();
final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>();
try {
User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy());
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(mParcelableProxy.getProxy());
User keybaseUser = User.findByFingerprint(keybaseQuery, fingerprint);
for (Proof proof : keybaseUser.getProofs()) {
Integer proofType = proof.getType();
appendIfOK(proofs, proofType, proof);
@@ -266,7 +271,12 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
} catch (KeybaseException ignored) {
}
return new ResultPage(getString(R.string.key_trust_results_prefix), proofList);
String prefix = "";
if (isAdded()) {
prefix = getString(R.string.key_trust_results_prefix);
}
return new ResultPage(prefix, proofList);
}
private SpannableStringBuilder formatSpannableString(SpannableStringBuilder proofLinks, String proofType) {
@@ -291,7 +301,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
if (haveProofFor(proof.getType())) {
ssb.append("\u00a0[");
startAt = ssb.length();
String verify = getString(R.string.keybase_verify);
String verify = "";
if (isAdded()) {
verify = getString(R.string.keybase_verify);
}
ssb.append(verify);
ClickableSpan clicker = new ClickableSpan() {
@Override
@@ -308,6 +321,11 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
@Override
protected void onPostExecute(ResultPage result) {
super.onPostExecute(result);
// stop if fragment is no longer added to an activity
if(!isAdded()) {
return;
}
if (result.mProofs.isEmpty()) {
result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence);
}
@@ -356,7 +374,12 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
default:
stringIndex = R.string.keybase_narrative_unknown;
}
return getActivity().getString(stringIndex);
if (isAdded()) {
return getString(stringIndex);
} else {
return "";
}
}
private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException {

View File

@@ -24,6 +24,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import android.app.Activity;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
@@ -44,6 +45,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -54,6 +56,7 @@ import com.squareup.okhttp.Request;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
@@ -68,11 +71,9 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
private static final String ARG_KEYSERVER = "arg_keyserver";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_VERIFICATION_FAILED = 2;
public static final String MESSAGE_KEYSERVER = "new_keyserver";
public static final String MESSAGE_VERIFIED = "verified";
public static final String MESSAGE_FAILURE_REASON = "failure_reason";
public static final String MESSAGE_KEYSERVER_DELETED = "keyserver_deleted";
public static final String MESSAGE_DIALOG_ACTION = "message_dialog_action";
public static final String MESSAGE_EDIT_POSITION = "keyserver_edited_position";
@@ -82,7 +83,9 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
private int mPosition;
private EditText mKeyserverEditText;
private TextInputLayout mKeyserverEditTextLayout;
private CheckBox mVerifyKeyserverCheckBox;
private CheckBox mOnlyTrustedKeyserverCheckBox;
public enum DialogAction {
ADD,
@@ -91,7 +94,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
public enum FailureReason {
INVALID_URL,
CONNECTION_FAILED
CONNECTION_FAILED,
NO_PINNED_CERTIFICATE
}
public static AddEditKeyserverDialogFragment newInstance(Messenger messenger,
@@ -126,7 +130,15 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
alert.setView(view);
mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text);
mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox);
mKeyserverEditTextLayout = (TextInputLayout) view.findViewById(R.id.keyserver_url_edit_text_layout);
mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_connection_checkbox);
mOnlyTrustedKeyserverCheckBox = (CheckBox) view.findViewById(R.id.only_trusted_keyserver_checkbox);
mVerifyKeyserverCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mOnlyTrustedKeyserverCheckBox.setEnabled(isChecked);
}
});
switch (mDialogAction) {
case ADD: {
@@ -212,6 +224,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mKeyserverEditTextLayout.setErrorEnabled(false);
// behaviour same for edit and add
final String keyserverUrl = mKeyserverEditText.getText().toString();
if (mVerifyKeyserverCheckBox.isChecked()) {
@@ -220,13 +234,20 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
@Override
public void onOrbotStarted() {
verifyConnection(keyserverUrl,
proxyPrefs.parcelableProxy.getProxy());
verifyConnection(
keyserverUrl,
proxyPrefs.parcelableProxy.getProxy(),
mOnlyTrustedKeyserverCheckBox.isChecked()
);
}
@Override
public void onNeutralButton() {
verifyConnection(keyserverUrl, null);
verifyConnection(
keyserverUrl,
null,
mOnlyTrustedKeyserverCheckBox.isChecked()
);
}
@Override
@@ -236,7 +257,11 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
};
if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy());
verifyConnection(
keyserverUrl,
proxyPrefs.parcelableProxy.getProxy(),
mOnlyTrustedKeyserverCheckBox.isChecked()
);
}
} else {
dismiss();
@@ -272,14 +297,28 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
sendMessageToHandler(MESSAGE_OKAY, data);
}
public void verificationFailed(FailureReason reason) {
Bundle data = new Bundle();
data.putSerializable(MESSAGE_FAILURE_REASON, reason);
public void verificationFailed(FailureReason failureReason) {
switch (failureReason) {
case CONNECTION_FAILED: {
mKeyserverEditTextLayout.setError(
getString(R.string.add_keyserver_connection_failed));
break;
}
case INVALID_URL: {
mKeyserverEditTextLayout.setError(
getString(R.string.add_keyserver_invalid_url));
break;
}
case NO_PINNED_CERTIFICATE: {
mKeyserverEditTextLayout.setError(
getString(R.string.add_keyserver_keyserver_not_trusted));
break;
}
}
sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data);
}
public void verifyConnection(String keyserver, final Proxy proxy) {
public void verifyConnection(String keyserver, final Proxy proxy, final boolean onlyTrustedKeyserver) {
new AsyncTask<String, Void, FailureReason>() {
ProgressDialog mProgressDialog;
@@ -288,7 +327,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
@Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_url));
mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_connection));
mProgressDialog.setCancelable(false);
mProgressDialog.show();
}
@@ -316,7 +355,18 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
Log.d("Converted URL", newKeyserver.toString());
OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy);
TlsHelper.pinCertificateIfNecessary(client, newKeyserver.toURL());
// don't follow any redirects
client.setFollowRedirects(false);
client.setFollowSslRedirects(false);
if (onlyTrustedKeyserver
&& !TlsHelper.usePinnedCertificateIfAvailable(client, newKeyserver.toURL())) {
Log.w(Constants.TAG, "No pinned certificate for this host in OpenKeychain's assets.");
reason = FailureReason.NO_PINNED_CERTIFICATE;
return reason;
}
client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute();
} catch (TlsHelper.TlsHelperException e) {
reason = FailureReason.CONNECTION_FAILED;

View File

@@ -1,234 +0,0 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
/**
* This is a file chooser dialog no longer used with KitKat
*/
public class FileDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_TITLE = "title";
private static final String ARG_MESSAGE = "message";
private static final String ARG_DEFAULT_FILE = "default_file";
private static final String ARG_CHECKBOX_TEXT = "checkbox_text";
public static final int MESSAGE_OKAY = 1;
public static final String MESSAGE_DATA_FILE = "file";
public static final String MESSAGE_DATA_CHECKED = "checked";
private Messenger mMessenger;
private EditText mFilename;
private ImageButton mBrowse;
private CheckBox mCheckBox;
private TextView mMessageTextView;
private File mFile;
private static final int REQUEST_CODE = 0x00007004;
/**
* Creates new instance of this file dialog fragment
*/
public static FileDialogFragment newInstance(Messenger messenger, String title, String message,
File defaultFile, String checkboxText) {
FileDialogFragment frag = new FileDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putString(ARG_TITLE, title);
args.putString(ARG_MESSAGE, message);
args.putString(ARG_DEFAULT_FILE, defaultFile.getAbsolutePath());
args.putString(ARG_CHECKBOX_TEXT, checkboxText);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
String title = getArguments().getString(ARG_TITLE);
String message = getArguments().getString(ARG_MESSAGE);
mFile = new File(getArguments().getString(ARG_DEFAULT_FILE));
if (!mFile.isAbsolute()) {
// We use OK dir by default
mFile = new File(Constants.Path.APP_DIR.getAbsolutePath(), mFile.getName());
}
String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT);
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(title);
View view = inflater.inflate(R.layout.file_dialog, null);
mMessageTextView = (TextView) view.findViewById(R.id.message);
mMessageTextView.setText(message);
mFilename = (EditText) view.findViewById(R.id.input);
mFilename.setText(mFile.getName());
mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
mBrowse.setVisibility(View.GONE);
} else {
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// only .asc or .gpg files
// setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
// or gpg types!
FileHelper.saveDocumentKitKat(
FileDialogFragment.this, "*/*", mFile.getName(), REQUEST_CODE);
}
});
}
mCheckBox = (CheckBox) view.findViewById(R.id.checkbox);
if (checkboxText == null) {
mCheckBox.setEnabled(false);
mCheckBox.setVisibility(View.GONE);
} else {
mCheckBox.setEnabled(true);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setText(checkboxText);
mCheckBox.setChecked(true);
}
alert.setView(view);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
String currentFilename = mFilename.getText().toString();
if (currentFilename == null || currentFilename.isEmpty()) {
// No file is like pressing cancel, UI: maybe disable positive button in this case?
return;
}
if (mFile == null || currentFilename.startsWith("/")) {
mFile = new File(currentFilename);
} else if (!mFile.getName().equals(currentFilename)) {
// We update our File object if user changed name!
mFile = new File(mFile.getParentFile(), currentFilename);
}
boolean checked = mCheckBox.isEnabled() && mCheckBox.isChecked();
// return resulting data back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_FILE, mFile.getAbsolutePath());
data.putBoolean(MESSAGE_DATA_CHECKED, checked);
sendMessageToHandler(MESSAGE_OKAY, data);
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
return alert.show();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
case REQUEST_CODE: {
if (resultCode == Activity.RESULT_OK && data != null) {
File file = new File(data.getData().getPath());
if (file.getParentFile().exists()) {
mFile = file;
mFilename.setText(mFile.getName());
} else {
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
}
}
break;
}
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
/**
* Send message back to handler which is initialized in a activity
*
* @param what Message integer you want to send
*/
private void sendMessageToHandler(Integer what, Bundle data) {
Message msg = Message.obtain();
msg.what = what;
if (data != null) {
msg.setData(data);
}
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}

View File

@@ -27,7 +27,6 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource;
@@ -35,7 +34,6 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.net.URI;
@@ -134,9 +132,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
String targetName = "pgpkey.txt";
// TODO: not supported on Android < 4.4
FileHelper.saveDocument(this,
targetName, Uri.fromFile(new File(Constants.Path.APP_DIR, targetName)),
"text/plain", R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to,
targetName,
"text/plain",
REQUEST_CODE_OUTPUT);
}

View File

@@ -103,8 +103,7 @@ public class EmailKeyHelper {
}
}
}
} catch (Keyserver.QueryFailedException ignored) {
} catch (Keyserver.QueryNeedsRepairException ignored) {
} catch (Keyserver.CloudSearchFailureException ignored) {
}
return new ArrayList<>(keys);
}

View File

@@ -69,13 +69,14 @@ public class ExportHelper
: R.string.specify_backup_dest_single);
}
FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
@Override
public void onFileSelected(File file, boolean checked) {
mExportFile = file;
exportKeys(masterKeyId == null ? null : new long[] { masterKeyId }, exportSecret);
}
}, mActivity.getSupportFragmentManager(), title, message, exportFile, null);
// TODO: for valodim
// FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
// @Override
// public void onFileSelected(File file, boolean checked) {
// mExportFile = file;
// exportKeys(masterKeyId == null ? null : new long[] { masterKeyId }, exportSecret);
// }
// }, mActivity.getSupportFragmentManager(), title, message, exportFile, null);
}
// TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.util;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,20 +29,13 @@ import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -82,50 +74,24 @@ import java.text.DecimalFormat;
public class FileHelper {
public static void openDocument(Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
openDocumentKitKat(fragment, mimeType, multiple, requestCode);
}
}
public static void saveDocument(Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, int requestCode) {
saveDocument(fragment, targetName, inputUri, "*/*", title, message, requestCode);
}
public static void saveDocument(Fragment fragment, String targetName, Uri inputUri, String mimeType,
@StringRes int title, @StringRes int message, int requestCode) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
saveDocumentDialog(fragment, targetName, inputUri, title, message, requestCode);
} else {
saveDocumentKitKat(fragment, mimeType, targetName, requestCode);
openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode);
}
}
public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, final int requestCode) {
saveDocumentDialog(fragment, targetName, inputUri, title, message, new FileDialogCallback() {
// is this a good idea? seems hacky...
@Override
public void onFileSelected(File file, boolean checked) {
Intent intent = new Intent();
intent.setData(Uri.fromFile(file));
fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent);
}
});
public static void saveDocument(Fragment fragment, String targetName, int requestCode) {
saveDocument(fragment, targetName, "*/*", requestCode);
}
public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, FileDialogCallback callback) {
File file = inputUri == null ? null : new File(inputUri.getPath());
File parentDir = file != null && file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
File targetFile = new File(parentDir, targetName);
saveDocumentDialog(callback, fragment.getActivity().getSupportFragmentManager(),
fragment.getString(title), fragment.getString(message), targetFile, null);
public static void saveDocument(Fragment fragment, String targetName, String mimeType,
int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
saveDocumentKitKat(fragment, mimeType, targetName, requestCode);
} else {
throw new RuntimeException("saveDocument does not support Android < 4.4!");
}
}
/** Opens the preferred installed file manager on Android and shows a toast
@@ -172,36 +138,6 @@ public class FileHelper {
fragment.startActivityForResult(intent, requestCode);
}
public static void saveDocumentDialog(
final FileDialogCallback callback, final FragmentManager fragmentManager,
final String title, final String message, final File defaultFile,
final String checkMsg) {
// Message is received after file is selected
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
callback.onFileSelected(
new File(message.getData().getString(FileDialogFragment.MESSAGE_DATA_FILE)),
message.getData().getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
}
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
@Override
public void run() {
FileDialogFragment fileDialog = FileDialogFragment.newInstance(messenger, title, message,
defaultFile, checkMsg);
fileDialog.show(fragmentManager, "fileDialog");
}
});
}
public static String getFilename(Context context, Uri uri) {
String filename = null;
try {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.util;

View File

@@ -141,6 +141,10 @@ public class NfcHelper {
}
protected void onPostExecute(Void unused) {
if (mActivity.isFinishing()) {
return;
}
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(mNdefCallback,
mActivity);

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.util;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.OkUrlFactory;
import com.textuality.keybase.lib.KeybaseUrlConnectionClient;
import org.sufficientlysecure.keychain.Constants;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.TimeUnit;
/**
* Wrapper for Keybase Lib
*/
public class OkHttpKeybaseClient implements KeybaseUrlConnectionClient {
private final OkUrlFactory factory;
private static OkUrlFactory generateUrlFactory() {
OkHttpClient client = new OkHttpClient();
return new OkUrlFactory(client);
}
public OkHttpKeybaseClient() {
factory = generateUrlFactory();
}
@Override
public URLConnection openConnection(URL url) throws IOException {
return openConnection(url, null);
}
@Override
public URLConnection openConnection(URL url, Proxy proxy) throws IOException {
if (proxy != null) {
factory.client().setProxy(proxy);
factory.client().setConnectTimeout(30000, TimeUnit.MILLISECONDS);
factory.client().setReadTimeout(40000, TimeUnit.MILLISECONDS);
} else {
factory.client().setConnectTimeout(5000, TimeUnit.MILLISECONDS);
factory.client().setReadTimeout(25000, TimeUnit.MILLISECONDS);
}
factory.client().setFollowSslRedirects(false);
// forced the usage of keybase.io pinned certificate
try {
if (!TlsHelper.usePinnedCertificateIfAvailable(factory.client(), url)) {
throw new IOException("no pinned certificate found for URL!");
}
} catch (TlsHelper.TlsHelperException e) {
Log.e(Constants.TAG, "TlsHelper failed", e);
throw new IOException("TlsHelper failed");
}
return factory.open(url);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.util;
import android.content.res.AssetManager;
import com.squareup.okhttp.OkHttpClient;
import org.sufficientlysecure.keychain.Constants;
import java.io.ByteArrayInputStream;
@@ -37,7 +38,6 @@ import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
@@ -49,15 +49,14 @@ public class TlsHelper {
}
}
private static Map<String, byte[]> sStaticCA = new HashMap<>();
private static Map<String, byte[]> sPinnedCertificates = new HashMap<>();
public static void addStaticCA(String domain, byte[] certificate) {
sStaticCA.put(domain, certificate);
}
public static void addStaticCA(String domain, AssetManager assetManager, String name) {
/**
* Add certificate from assets to pinned certificate map.
*/
public static void addPinnedCertificate(String host, AssetManager assetManager, String cerFilename) {
try {
InputStream is = assetManager.open(name);
InputStream is = assetManager.open(cerFilename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int reads = is.read();
@@ -68,27 +67,36 @@ public class TlsHelper {
is.close();
addStaticCA(domain, baos.toByteArray());
sPinnedCertificates.put(host, baos.toByteArray());
} catch (IOException e) {
Log.w(Constants.TAG, e);
}
}
public static void pinCertificateIfNecessary(OkHttpClient client, URL url) throws TlsHelperException, IOException {
/**
* Use pinned certificate for OkHttpClient if we have one.
*
* @return true, if certificate is available, false if not
* @throws TlsHelperException
* @throws IOException
*/
public static boolean usePinnedCertificateIfAvailable(OkHttpClient client, URL url) throws TlsHelperException, IOException {
if (url.getProtocol().equals("https")) {
for (String domain : sStaticCA.keySet()) {
if (url.getHost().endsWith(domain)) {
pinCertificate(sStaticCA.get(domain), client);
// use certificate PIN from assets if we have one
for (String host : sPinnedCertificates.keySet()) {
if (url.getHost().endsWith(host)) {
pinCertificate(sPinnedCertificates.get(host), client);
return true;
}
}
}
return false;
}
/**
* Modifies the client to accept only requests with a given certificate. Applies to all URLs requested by the
* client.
* Therefore a client that is pinned this way should be used to only make requests to URLs with passed certificate.
* TODO: Refactor - More like SSH StrictHostKeyChecking than pinning?
*
* @param certificate certificate to pin
* @param client OkHttpClient to enforce pinning on
@@ -97,8 +105,10 @@ public class TlsHelper {
*/
private static void pinCertificate(byte[] certificate, OkHttpClient client)
throws TlsHelperException, IOException {
// We don't use OkHttp's CertificatePinner since it depends on a TrustManager to verify it too. Refer to
// note at end of description: http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html
// We don't use OkHttp's CertificatePinner since it can not be used to pin self-signed
// certificate if such certificate is not accepted by TrustManager.
// (Refer to note at end of description:
// http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html )
// Creating our own TrustManager that trusts only our certificate eliminates the need for certificate pinning
try {
// Load CA
@@ -126,42 +136,4 @@ public class TlsHelper {
}
}
/**
* Opens a Connection that will only accept certificates signed with a specific CA and skips common name check.
* This is required for some distributed Keyserver networks like sks-keyservers.net
*
* @param certificate The X.509 certificate used to sign the servers certificate
* @param url Connection target
*/
public static HttpsURLConnection openCAConnection(byte[] certificate, URL url)
throws TlsHelperException, IOException {
try {
// Load CA
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(new ByteArrayInputStream(certificate));
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
} catch (CertificateException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
throw new TlsHelperException(e);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -9,21 +9,37 @@
android:paddingRight="24dp"
android:paddingTop="16dp">
<EditText
android:id="@+id/keyserver_url_edit_text"
<android.support.design.widget.TextInputLayout
android:id="@+id/keyserver_url_edit_text_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
android:ems="10"
android:hint="@string/label_enter_keyserver_url"
android:imeOptions="actionDone" />
android:layout_marginBottom="8dp">
<EditText
android:id="@+id/keyserver_url_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ems="10"
android:hint="@string/label_enter_keyserver_url"
android:imeOptions="actionDone"
android:inputType="textUri" />
</android.support.design.widget.TextInputLayout>
<CheckBox
android:id="@+id/verify_keyserver_checkbox"
android:id="@+id/verify_connection_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_verify_keyserver"
android:checked="true"/>
android:checked="true"
android:text="@string/label_verify_keyserver_connection" />
<CheckBox
android:id="@+id/only_trusted_keyserver_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/label_only_trusted_keyserver" />
</LinearLayout>

View File

@@ -1,155 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/certify_fingerprint_buttons_divider">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/certify_fingerprint_intro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/certify_fingerprint_text" />
<android.support.v7.widget.CardView
android:id="@+id/certify_fingerprint_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="?attr/colorCardViewBackground"
app:cardUseCompatPadding="true"
app:cardCornerRadius="4dp"
android:layout_gravity="top">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="@style/CardViewHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_fingerprint" />
<TextView
android:id="@+id/certify_fingerprint_fingerprint"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:typeface="monospace"
android:gravity="center_vertical" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:id="@+id/certify_fingerprint_buttons">
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/certify_fingerprint_button_no"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/certify_fingerprint_intro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_no"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle"
android:layout_gravity="center_vertical" />
android:layout_marginBottom="32dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="@string/certify_fingerprint_text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/certify_fingerprint_button_yes"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
<android.support.v7.widget.CardView
android:id="@+id/certify_fingerprint_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_match"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle"
android:layout_gravity="center_vertical" />
android:layout_gravity="top"
app:cardBackgroundColor="?attr/colorCardViewBackground"
app:cardCornerRadius="4dp"
app:cardElevation="8dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/certify_fingerprint_fingerprint_header"
style="@style/CardViewHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_fingerprint" />
<TextView
android:id="@+id/certify_fingerprint_fingerprint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="24dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:textSize="18sp"
android:typeface="monospace" />
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/certify_fingerprint_button_no"
style="?android:attr/borderlessButtonStyle"
android:textColor="@color/android_red_dark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:drawableLeft="@drawable/ic_close_black_24dp"
android:drawablePadding="8dp"
android:gravity="left|center_vertical"
android:minHeight="48dp"
android:text="@string/btn_not_matching"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/certify_fingerprint_button_yes"
style="?android:attr/borderlessButtonStyle"
android:textColor="@color/android_green_dark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:clickable="true"
android:drawableLeft="@drawable/ic_check_black_24dp"
android:drawablePadding="8dp"
android:gravity="left|center_vertical"
android:minHeight="48dp"
android:text="@string/btn_match"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
<View
android:id="@+id/certify_fingerprint_buttons_divider2"
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:layout_alignBottom="@+id/certify_fingerprint_buttons_text"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:id="@+id/certify_fingerprint_buttons_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/certify_fingerprint_text2"
android:layout_above="@+id/certify_fingerprint_buttons"
android:layout_centerHorizontal="true" />
<View
android:id="@+id/certify_fingerprint_buttons_divider"
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:layout_alignTop="@+id/certify_fingerprint_buttons_text"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
</ScrollView>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/decrypt_open"
android:icon="@drawable/ic_apps_black_24dp"
android:title="@string/btn_open_with" />
<item
android:id="@+id/decrypt_share"
android:icon="@drawable/ic_share_black_24dp"
android:title="@string/btn_share_decrypted_text" />
<item
android:id="@+id/decrypt_save"
android:icon="@drawable/ic_save_black_24dp"
android:title="@string/btn_save" />
</menu>

View File

@@ -3,17 +3,12 @@
<item
android:id="@+id/decrypt_open"
android:title="Open with…"
android:icon="@drawable/ic_apps_black_24dp" />
android:icon="@drawable/ic_apps_black_24dp"
android:title="@string/btn_open_with" />
<item
android:id="@+id/decrypt_share"
android:title="@string/btn_share_decrypted_text"
android:icon="@drawable/ic_share_black_24dp" />
android:icon="@drawable/ic_share_black_24dp"
android:title="@string/btn_share_decrypted_text" />
<item
android:id="@+id/decrypt_save"
android:title="@string/btn_save"
android:icon="@drawable/ic_save_black_24dp" />
</menu>
</menu>

View File

@@ -41,7 +41,7 @@
android:id="@+id/menu_key_view_certify_fingerprint_word"
app:showAsAction="never"
android:visible="false"
android:title="@string/menu_certify_fingerprint_word" />
android:title="@string/menu_certify_fingerprint_phrases" />
<item
android:id="@+id/menu_key_view_add_linked_identity"

View File

@@ -0,0 +1,128 @@
able
angry
bad
bent
bitter
black
blue
boiling
bright
broken
brown
certain
cheap
clean
clear
cold
common
complex
cruel
dark
dead
dear
deep
dirty
dry
early
elastic
equal
false
fat
feeble
female
fertile
first
fixed
flat
foolish
free
full
future
general
good
great
green
grey
hanging
happy
hard
healthy
high
hollow
kind
last
late
lazy
left
like
living
long
loose
loud
low
male
married
medical
mixed
narrow
natural
new
normal
old
open
past
poor
present
pretty
private
public
quick
quiet
ready
rare
red
regular
right
rough
round
sad
safe
same
second
secret
serious
sharp
short
shut
sick
simple
slow
small
smooth
soft
solid
sour
special
sticky
stiff
strange
strong
sudden
sweet
tall
thick
thin
tight
tired
true
unknown
violent
waiting
warm
wet
white
wide
wise
wrong
yellow
young

View File

@@ -0,0 +1,64 @@
ably
angrily
badly
bitterly
brightly
brokenly
cheaply
clearly
coldly
commonly
cruelly
darkly
dearly
deeply
drily
equally
falsely
feebly
fixedly
flatly
freely
fully
greatly
happily
hardly
kindly
lately
lazily
loosely
loudly
narrowly
newly
normally
openly
poorly
prettily
publicly
quickly
quietly
readily
rarely
roughly
sadly
safely
secretly
sharply
simply
slowly
smoothly
softly
solidly
sourly
stiffly
strongly
suddenly
sweetly
thickly
thinly
tightly
tiredly
truly
warmly
widely
wisely

View File

@@ -0,0 +1,8 @@
her
his
my
our
that
the
this
your

View File

@@ -0,0 +1,512 @@
account
air
amount
angle
animal
answer
ant
apple
arch
arm
army
attack
attempt
baby
back
bag
ball
band
base
basin
basket
bath
bed
bee
belief
bell
berry
bird
birth
bite
blade
blood
blow
board
boat
body
bone
book
boot
bottle
box
boy
brain
brake
branch
bread
breath
brick
bridge
brother
brush
bucket
bulb
burn
butter
button
cake
camera
canvas
car
card
cat
cause
chain
chalk
chance
change
cheese
chest
chin
church
circle
clock
cloth
cloud
coal
coat
collar
colour
comb
comfort
company
control
cook
copper
copy
cord
cork
cotton
cough
country
cover
cow
crack
credit
crime
crush
cry
cup
current
curtain
curve
cushion
damage
danger
day
debt
degree
design
desire
detail
disease
disgust
dog
door
doubt
drain
drawer
dress
drink
drop
dust
ear
earth
edge
effect
egg
end
engine
error
event
example
expert
eye
face
fact
fall
family
farm
father
fear
feather
feeling
fiction
field
fight
finger
fire
fish
flag
flame
flight
floor
flower
fly
fold
food
foot
force
fork
form
fowl
frame
friend
front
fruit
garden
girl
glass
glove
goat
gold
grain
grass
grip
group
growth
guide
gun
hair
hammer
hand
harbour
harmony
hat
hate
head
heart
heat
history
hole
hook
hope
horn
horse
hour
house
humour
ice
idea
impulse
ink
insect
iron
island
jelly
jewel
join
journey
judge
jump
kettle
key
kick
kiss
knee
knife
knot
land
laugh
law
lead
leaf
leather
leg
letter
level
library
lift
light
limit
line
linen
lip
liquid
list
lock
look
loss
love
machine
man
manager
map
mark
market
mass
match
meal
measure
meat
meeting
memory
metal
middle
milk
mind
mine
minute
mist
money
monkey
month
moon
morning
mother
motion
mouth
move
muscle
music
nail
name
nation
neck
need
needle
nerve
net
news
night
noise
nose
note
number
nut
offer
office
oil
opinion
orange
order
oven
owner
page
pain
paint
paper
parcel
part
paste
payment
peace
pen
pencil
person
picture
pig
pin
pipe
place
plane
plant
plate
play
plough
pocket
point
poison
polish
porter
pot
potato
powder
power
price
print
prison
process
produce
profit
prose
protest
pull
pump
purpose
push
quality
rail
rain
range
rat
rate
ray
reason
receipt
record
regret
request
respect
rest
reward
rhythm
rice
ring
river
road
rod
roll
roof
room
root
rub
rule
run
sail
salt
sand
scale
school
science
screw
sea
seat
seed
self
sense
servant
sex
shade
shake
shame
sheep
shelf
ship
shirt
shock
shoe
side
sign
silk
silver
sister
size
skin
skirt
sky
sleep
slip
slope
smash
smell
smile
smoke
snake
sneeze
snow
soap
society
sock
son
song
sort
sound
soup
space
spade
sponge
spoon
spring
square
stage
stamp
star
start
station
steam
steel
stem
step
stick
stitch
stomach
stone
stop
store
story
street
stretch
sugar
summer
sun
support
swim
system
table
tail
talk
taste
tax
test
theory
thing
thought
thread
throat
thumb
thunder
ticket
time
tin
toe
tongue
tooth
top
touch
town
trade
train
tray
tree
trick
trouble
turn
twist
unit
value
verse
vessel
view
voice
walk
wall
war
wash
waste
watch
water
wave
wax
way
weather
week
weight
wheel
whip
whistle
wind
window
wine
wing
winter
wire
woman
wood
wool
word
work
worm
wound
writing
year

View File

@@ -0,0 +1,32 @@
above
across
after
against
along
among
around
at
before
behind
below
beneath
beside
between
beyond
by
from
in
inside
into
near
on
outside
over
past
round
through
to
towards
under
upon
with

View File

@@ -0,0 +1,128 @@
agrees
allows
answers
arrives
asks
is
becomes
begins
believes
brings
burns
buys
calls
changes
chooses
cleans
closes
comes
compares
continues
cooks
costs
counts
cries
cuts
dances
decides
describes
destroys
dies
does
drinks
drives
eats
ends
explains
falls
feels
fights
fills
finds
finishes
forgets
forgives
gets
gives
goes
grows
hates
has
hears
helps
hides
holds
hurts
improves
jumps
keeps
kills
knows
laughs
learns
leaves
lets
lies
listens
lives
looks
loses
loves
makes
meets
moves
needs
occurs
offers
opens
pays
plays
prefers
prepares
presses
promises
pulls
pushes
puts
reads
receives
remembers
repeats
rests
returns
runs
sees
sells
sends
shouts
shows
sings
sits
sleeps
smiles
speaks
starts
stays
stops
studies
suggests
supports
takes
talks
teaches
tells
thinks
throws
touches
travels
treats
tries
turns
uses
visits
walks
wants
washes
wins
works
writes

View File

@@ -0,0 +1,128 @@
agrees with
allows
answers
arrives at
asks
is
becomes
begins
believes
brings
burns
buys
calls
changes
chooses
cleans
closes
comes to
compares
continues
cooks
costs
counts
cries for
cuts
dances with
decides on
describes
destroys
dies for
does
drinks
drives
eats
ends
explains
falls on
feels
fights
fills
finds
finishes
forgets
forgives
gets
gives
goes to
grows
hates
has
hears
helps
hides
holds
hurts
improves
jumps over
keeps
kills
knows
laughs at
learns
leaves
lets
lies to
listens to
lives with
looks at
loses
loves
makes
meets
moves
needs
occurs to
offers
opens
pays
plays
prefers
prepares
presses
promises
pulls
pushes
puts
reads
receives
remembers
repeats
rests by
returns
runs to
sees
sells
sends
shouts at
shows
sings to
sits by
sleeps by
smiles at
speaks to
starts
stays with
stops
studies
suggests
supports
takes
talks to
teaches
tells
thinks of
throws
touches
travels to
treats
tries
turns
uses
visits
walks to
wants
washes
wins
works for
writes to

View File

@@ -135,7 +135,7 @@
<string name="label_enable_compression">Zapnout kompresi</string>
<string name="label_encrypt_filenames">Zašifrovat jména souborů</string>
<string name="label_hidden_recipients">Skrýt příjemce</string>
<string name="label_verify_keyserver">Ověřit keyserver</string>
<string name="label_verify_keyserver_connection">Ověřit keyserver</string>
<string name="label_enter_keyserver_url">Zadejte URL keyserveru</string>
<string name="pref_keyserver">OpenPGP keyserver</string>
<string name="pref_keyserver_summary">Vyhledat klíče na vybraném OpenPGP keyserveru (protokol HKP)</string>
@@ -496,7 +496,7 @@
<string name="view_key_fragment_no_system_contact">&lt;žádný&gt;</string>
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Přidat keyserver</string>
<string name="add_keyserver_verified">Keyserver ověřen!</string>
<string name="add_keyserver_connection_verified">Keyserver ověřen!</string>
<string name="add_keyserver_without_verification">Keyserver přidán bez verifikace.</string>
<string name="add_keyserver_invalid_url">Neplatná URL!</string>
<string name="add_keyserver_connection_failed">Nepodařilo se připojit ke key severu. Prosím ověřte URL a vaše připojení k internetu.</string>

View File

@@ -155,7 +155,7 @@
<string name="label_enable_compression">Komprimierung aktivieren</string>
<string name="label_encrypt_filenames">Dateinamen verschlüsseln</string>
<string name="label_hidden_recipients">Empfänger verbergen</string>
<string name="label_verify_keyserver">Schlüsselserver verifizieren</string>
<string name="label_verify_keyserver_connection">Schlüsselserver verifizieren</string>
<string name="label_enter_keyserver_url">Schlüsselserver-URL eingeben</string>
<string name="label_keyserver_dialog_delete">Schlüsselserver löschen</string>
<string name="label_theme">Design</string>
@@ -386,7 +386,7 @@
<string name="progress_deleting">Lösche Schlüssel…</string>
<string name="progress_con_saving">Zusammenführung: Sichere in den Zwischenspeicher...</string>
<string name="progress_con_reimport">Zusammenführung: Reimportiere...</string>
<string name="progress_verifying_keyserver_url">Schlüsselserver wird verifiziert…</string>
<string name="progress_verifying_keyserver_connection">Schlüsselserver wird verifiziert…</string>
<string name="progress_starting_orbot">Orbot wird gestartet…</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Via Name, E-Mail suchen...</string>
@@ -691,7 +691,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Schlüsselserver hinzufügen</string>
<string name="edit_keyserver_dialog_title">Schlüsselserver bearbeiten</string>
<string name="add_keyserver_verified">Schlüsselserver verifiziert!</string>
<string name="add_keyserver_connection_verified">Schlüsselserver verifiziert!</string>
<string name="add_keyserver_without_verification">Schlüsselserver ohne Verifikation hinzugefügt.</string>
<string name="add_keyserver_invalid_url">Ungültige URL!</string>
<string name="add_keyserver_connection_failed">Verbindung zum Schlüsselserver fehlgeschlagen. Bitte überprüfe die URL und deine Internetverbindung.</string>

View File

@@ -155,7 +155,7 @@
<string name="label_enable_compression">Habilitar compresión</string>
<string name="label_encrypt_filenames">Cifrar nombres de ficheros</string>
<string name="label_hidden_recipients">Ocultar receptores</string>
<string name="label_verify_keyserver">Verificar servidor de claves</string>
<string name="label_verify_keyserver_connection">Verificar servidor de claves</string>
<string name="label_enter_keyserver_url">Introduzca URL de servidor de claves</string>
<string name="label_keyserver_dialog_delete">Borrar servidor de claves</string>
<string name="label_theme">Tema decorativo</string>
@@ -386,7 +386,7 @@
<string name="progress_deleting">borrando claves...</string>
<string name="progress_con_saving">consolidación: guardando en caché...</string>
<string name="progress_con_reimport">consolidación: reimportando</string>
<string name="progress_verifying_keyserver_url">verificando servidor de claves...</string>
<string name="progress_verifying_keyserver_connection">verificando servidor de claves...</string>
<string name="progress_starting_orbot">Iniciando Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Buscar mediante Nombre, Correo electrónico...</string>
@@ -691,7 +691,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Añadir servidor de claves</string>
<string name="edit_keyserver_dialog_title">Editar servidor de claves</string>
<string name="add_keyserver_verified">¡Servidor de claves verificado!</string>
<string name="add_keyserver_connection_verified">¡Servidor de claves verificado!</string>
<string name="add_keyserver_without_verification">Servidor de claves añadido sin verificación</string>
<string name="add_keyserver_invalid_url">¡URL no válida!</string>
<string name="add_keyserver_connection_failed">Fallo al conectar al servidor de claves. Por favor, compuebe la URL y su conexión a Internet.</string>

View File

@@ -154,7 +154,7 @@
<string name="label_enable_compression">Gaitu konpresioa</string>
<string name="label_encrypt_filenames">Enkriptatu agirizenak</string>
<string name="label_hidden_recipients">Ezkutatu jasotzaileak</string>
<string name="label_verify_keyserver">Egiaztatu giltza-zerbitzaria</string>
<string name="label_verify_keyserver_connection">Egiaztatu giltza-zerbitzaria</string>
<string name="label_enter_keyserver_url">Sartu giltza-zerbitzariaren URL-a</string>
<string name="label_keyserver_dialog_delete">Ezabatu giltza-zerbitzaria</string>
<string name="label_theme">Azalgaia</string>
@@ -376,7 +376,7 @@
<string name="progress_deleting">giltzak ezabatzen...</string>
<string name="progress_con_saving">sendotu: katxean gordetzen...</string>
<string name="progress_con_reimport">sendotu: berrinportatzen...</string>
<string name="progress_verifying_keyserver_url">giltza-zerbitzaria egiaztatzen...</string>
<string name="progress_verifying_keyserver_connection">giltza-zerbitzaria egiaztatzen...</string>
<string name="progress_starting_orbot">Orbot Abiarazten...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Bilatu Izena, Post@... bidez</string>
@@ -675,7 +675,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Gehitu giltza-zerbitzaria</string>
<string name="edit_keyserver_dialog_title">Editatu giltza-zerbitzaria</string>
<string name="add_keyserver_verified">Giltza-zerbitzaria egiaztatuta!</string>
<string name="add_keyserver_connection_verified">Giltza-zerbitzaria egiaztatuta!</string>
<string name="add_keyserver_without_verification">Giltza-zerbitzaria gehituta egiaztapen gabe.</string>
<string name="add_keyserver_invalid_url">URL baliogabea!</string>
<string name="add_keyserver_connection_failed">Hutsegitea giltza-zerbitzariarekin elkartzerakoan. Mesedez egiaztatu URL-a eta zure internet elkarketa.</string>

View File

@@ -146,7 +146,7 @@
<string name="label_enable_compression">فشرده‌کردن</string>
<string name="label_encrypt_filenames">رمزگذاری اسمِ فایل‌ها</string>
<string name="label_hidden_recipients">مخفی‌کردن گیرنده‌ها</string>
<string name="label_verify_keyserver">بررسی سرورِ کلیدها</string>
<string name="label_verify_keyserver_connection">بررسی سرورِ کلیدها</string>
<string name="label_enter_keyserver_url">آدرس URL سرورِ کلید را وارد کنید</string>
<string name="label_keyserver_dialog_delete">حذف سرورهای کلید</string>
<string name="label_theme">قالب</string>

View File

@@ -155,7 +155,7 @@
<string name="label_enable_compression">Activer la compression</string>
<string name="label_encrypt_filenames">Chiffrer les nom de fichier</string>
<string name="label_hidden_recipients">Cacher les destinataires</string>
<string name="label_verify_keyserver">Vérifier le serveur de clefs</string>
<string name="label_verify_keyserver_connection">Vérifier le serveur de clefs</string>
<string name="label_enter_keyserver_url">Saisir l\'URL du serveur de clefs</string>
<string name="label_keyserver_dialog_delete">Supprimer le serveur de clefs</string>
<string name="label_theme">Thème</string>
@@ -386,7 +386,7 @@
<string name="progress_deleting">suppression des clefs...</string>
<string name="progress_con_saving">consolider : enregistrement dans le cache...</string>
<string name="progress_con_reimport">consolider : réimportation...</string>
<string name="progress_verifying_keyserver_url">vérification du serveur de clefs...</string>
<string name="progress_verifying_keyserver_connection">vérification du serveur de clefs...</string>
<string name="progress_starting_orbot">Démarrage d\'Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Chercher par nom, adresse courriel...</string>
@@ -691,7 +691,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Ajouter un serveur de clefs</string>
<string name="edit_keyserver_dialog_title">Modifier le serveur de clefs</string>
<string name="add_keyserver_verified">Le serveur de clefs a été vérifié !</string>
<string name="add_keyserver_connection_verified">Le serveur de clefs a été vérifié !</string>
<string name="add_keyserver_without_verification">Le serveur de clefs a été ajouté sans vérification.</string>
<string name="add_keyserver_invalid_url">URL invalide !</string>
<string name="add_keyserver_connection_failed">Échec de connexion au serveur de clefs. Veuillez vérifier l\'URL et votre connexion Internet.</string>

View File

@@ -149,7 +149,7 @@
<string name="label_enable_compression">Abilitare compressione</string>
<string name="label_encrypt_filenames">Codifica nome dei file</string>
<string name="label_hidden_recipients">Nascondi destinatari</string>
<string name="label_verify_keyserver">Verificare server chiavi</string>
<string name="label_verify_keyserver_connection">Verificare server chiavi</string>
<string name="label_enter_keyserver_url">Inserisci URL server chiavi</string>
<string name="label_keyserver_dialog_delete">Cancella server chiavi</string>
<string name="pref_keyserver">Server chiavi OpenPGP</string>

View File

@@ -155,7 +155,7 @@
<string name="label_enable_compression">圧縮を有効</string>
<string name="label_encrypt_filenames">暗号化するファイル名</string>
<string name="label_hidden_recipients">受信者を隠す</string>
<string name="label_verify_keyserver">鍵サーバを検証</string>
<string name="label_verify_keyserver_connection">鍵サーバを検証</string>
<string name="label_enter_keyserver_url">鍵サーバのURLを入力</string>
<string name="label_keyserver_dialog_delete">鍵サーバの削除</string>
<string name="label_theme">テーマ</string>
@@ -382,7 +382,7 @@
<string name="progress_deleting">鍵の削除中...</string>
<string name="progress_con_saving">統合: キャッシュへ保存…</string>
<string name="progress_con_reimport">統合: 再インポート中…</string>
<string name="progress_verifying_keyserver_url">鍵サーバの検証...</string>
<string name="progress_verifying_keyserver_connection">鍵サーバの検証...</string>
<string name="progress_starting_orbot">Orbotを始める...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">名前、Email...で検索</string>
@@ -674,7 +674,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">鍵サーバを追加</string>
<string name="edit_keyserver_dialog_title">鍵サーバの編集</string>
<string name="add_keyserver_verified">鍵サーバを検証しました!</string>
<string name="add_keyserver_connection_verified">鍵サーバを検証しました!</string>
<string name="add_keyserver_without_verification">鍵サーバを検証なしで追加した。</string>
<string name="add_keyserver_invalid_url">無効なURLです!</string>
<string name="add_keyserver_connection_failed">鍵サーバへの接続し失敗。URLとあなたのインターネット接続をチェックしてください。</string>

View File

@@ -151,7 +151,7 @@
<string name="label_enable_compression">Compressie aanzetten</string>
<string name="label_encrypt_filenames">Versleutel bestandsnamen</string>
<string name="label_hidden_recipients">Verberg ontvangers</string>
<string name="label_verify_keyserver">Sleutelserver verifiëren</string>
<string name="label_verify_keyserver_connection">Sleutelserver verifiëren</string>
<string name="label_enter_keyserver_url">Voer sleutelserver-URL in</string>
<string name="label_keyserver_dialog_delete">Sleutelserver verwijderen</string>
<string name="label_theme">Thema</string>
@@ -366,7 +366,7 @@
<string name="progress_deleting">bezig met verwijderen van sleutels…</string>
<string name="progress_con_saving">consolidatie: bezig met opslaan naar cache…</string>
<string name="progress_con_reimport">consolidatie: bezig met opnieuw importeren…</string>
<string name="progress_verifying_keyserver_url">bezig met verifiëren van sleutelserver…</string>
<string name="progress_verifying_keyserver_connection">bezig met verifiëren van sleutelserver…</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Zoeken via naam, e-mail, ...</string>
<!--key bit length selections-->
@@ -648,7 +648,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Sleutelserver toevoegen</string>
<string name="edit_keyserver_dialog_title">Sleutelserver bewerken</string>
<string name="add_keyserver_verified">Sleutelserver geverifieerd!</string>
<string name="add_keyserver_connection_verified">Sleutelserver geverifieerd!</string>
<string name="add_keyserver_without_verification">Sleutelserver toegevoegd zonder verificatie.</string>
<string name="add_keyserver_invalid_url">Ongeldige URL!</string>
<string name="add_keyserver_connection_failed">Kon niet verbinden met sleutelserver. Controleer de URL en je internetverbinding.</string>

View File

@@ -152,7 +152,7 @@
<string name="label_enable_compression">Использовать сжатие</string>
<string name="label_encrypt_filenames">Шифровать имена файлов</string>
<string name="label_hidden_recipients">Скрыть получателей</string>
<string name="label_verify_keyserver">Подтвердить сервер ключей</string>
<string name="label_verify_keyserver_connection">Подтвердить сервер ключей</string>
<string name="label_enter_keyserver_url">Введите адрес сервера ключей</string>
<string name="label_keyserver_dialog_delete">Удалить сервер ключей</string>
<string name="label_theme">Тема</string>
@@ -364,7 +364,7 @@
<string name="progress_deleting">удаление ключей...</string>
<string name="progress_con_saving">объединение: сохранение в кэш...</string>
<string name="progress_con_reimport">объединение: реимпорт...</string>
<string name="progress_verifying_keyserver_url">подтверждение сервера ключей...</string>
<string name="progress_verifying_keyserver_connection">подтверждение сервера ключей...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Искать через Имя, Email...</string>
<!--key bit length selections-->
@@ -568,7 +568,7 @@
<string name="view_key_fragment_no_system_contact">&lt;нет&gt;</string>
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Добавить сервер ключей</string>
<string name="add_keyserver_verified">Сервер ключей подтверждён!</string>
<string name="add_keyserver_connection_verified">Сервер ключей подтверждён!</string>
<string name="add_keyserver_without_verification">Сервер ключей добавлен без подтверждения.</string>
<string name="add_keyserver_invalid_url">Неправильный адрес!</string>
<!--Navigation Drawer-->

View File

@@ -155,7 +155,7 @@
<string name="label_enable_compression">Омогући компресију</string>
<string name="label_encrypt_filenames">Шифруј имена фајлова</string>
<string name="label_hidden_recipients">Сакриј примаоце</string>
<string name="label_verify_keyserver">Овери сервер кључева</string>
<string name="label_verify_keyserver_connection">Овери сервер кључева</string>
<string name="label_enter_keyserver_url">Унесите УРЛ сервера кључева</string>
<string name="label_keyserver_dialog_delete">Обриши сервер кључева</string>
<string name="label_theme">Тема</string>
@@ -390,7 +390,7 @@
<string name="progress_deleting">бришем кључеве…</string>
<string name="progress_con_saving">учвршћивање: уписујем у кеш…</string>
<string name="progress_con_reimport">учвршћивање: поново увозим…</string>
<string name="progress_verifying_keyserver_url">оверавам сервер кључева…</string>
<string name="progress_verifying_keyserver_connection">оверавам сервер кључева…</string>
<string name="progress_starting_orbot">Покрећем Орбот…</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Тражи преко имена, е-адресе…</string>
@@ -709,7 +709,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Додај сервер кључева</string>
<string name="edit_keyserver_dialog_title">Промени сервер кључева</string>
<string name="add_keyserver_verified">Сервер кључева оверен!</string>
<string name="add_keyserver_connection_verified">Сервер кључева оверен!</string>
<string name="add_keyserver_without_verification">Сервер кључева додат без оверивања.</string>
<string name="add_keyserver_invalid_url">Неисправан УРЛ!</string>
<string name="add_keyserver_connection_failed">Неуспех повезивања са сервером кључева. Проверите УРЛ и вашу везу са интернетом.</string>

View File

@@ -135,7 +135,7 @@
<string name="label_enable_compression">Aktivera kompression</string>
<string name="label_encrypt_filenames">Kryptera filnamn</string>
<string name="label_hidden_recipients">Dölj mottagare</string>
<string name="label_verify_keyserver">Verifiera nyckelserver</string>
<string name="label_verify_keyserver_connection">Verifiera nyckelserver</string>
<string name="label_enter_keyserver_url">Ange nyckelserver-URL</string>
<string name="pref_keyserver">OpenPGP nyckelservrar</string>
<string name="pref_keyserver_summary">Sök nycklar på valda OpenPGP nyckelservrar (HKP-protokollet)</string>
@@ -313,7 +313,7 @@
<string name="progress_deleting">raderar nycklar…</string>
<string name="progress_con_saving">konsolidera: sparar till cache…</string>
<string name="progress_con_reimport">konsolidera: återimporterar…</string>
<string name="progress_verifying_keyserver_url">verifierar nyckelserver...</string>
<string name="progress_verifying_keyserver_connection">verifierar nyckelserver...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Söker via Namn, E-post...</string>
<!--key bit length selections-->
@@ -565,7 +565,7 @@
<string name="view_key_fragment_no_system_contact">&lt;ingen&gt;</string>
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">Lägg till nyckelserver</string>
<string name="add_keyserver_verified">Nyckelserver verifierad!</string>
<string name="add_keyserver_connection_verified">Nyckelserver verifierad!</string>
<string name="add_keyserver_without_verification">Nyckelserver tillagd utan verifiering.</string>
<string name="add_keyserver_invalid_url">Ogiltig URL!</string>
<string name="add_keyserver_connection_failed">Misslyckades med att ansluta till nyckelserver. Kontrollera URL:en och din internetanslutning.</string>

View File

@@ -149,7 +149,7 @@
<string name="label_enable_compression">啓用壓縮</string>
<string name="label_encrypt_filenames">加密檔名</string>
<string name="label_hidden_recipients">隱藏收件人</string>
<string name="label_verify_keyserver">驗證金鑰伺服器</string>
<string name="label_verify_keyserver_connection">驗證金鑰伺服器</string>
<string name="label_enter_keyserver_url">輸入金鑰伺服器網址</string>
<string name="label_keyserver_dialog_delete">刪除金鑰伺服器</string>
<string name="label_theme">主題</string>
@@ -362,7 +362,7 @@
<string name="progress_verifying_integrity">正在驗證完整性…</string>
<string name="progress_deleting_securely">正在安全地刪除 \'%s\'...</string>
<string name="progress_deleting">正在刪除金鑰…</string>
<string name="progress_verifying_keyserver_url">正在驗證金鑰伺服器...</string>
<string name="progress_verifying_keyserver_connection">正在驗證金鑰伺服器...</string>
<string name="progress_starting_orbot">正在啟動Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">使用姓名,電子郵件尋找...</string>
@@ -625,7 +625,7 @@
<!--Add/Edit keyserver-->
<string name="add_keyserver_dialog_title">新增金鑰伺服器</string>
<string name="edit_keyserver_dialog_title">編輯金鑰伺服器</string>
<string name="add_keyserver_verified">已驗證金鑰伺服器!</string>
<string name="add_keyserver_connection_verified">已驗證金鑰伺服器!</string>
<string name="add_keyserver_without_verification">已新增金鑰伺服器但並未進行驗證。</string>
<string name="add_keyserver_invalid_url">URL無效</string>
<string name="add_keyserver_connection_failed">連線到金鑰伺服器失敗。請確認金鑰伺服器網址及網路連線。</string>

View File

@@ -63,6 +63,7 @@
<string name="section_share_key">"Key"</string>
<string name="section_key_server">"Keyserver"</string>
<string name="section_fingerprint">"Fingerprint"</string>
<string name="section_phrases">"Phrases"</string>
<string name="section_encrypt">"Encrypt"</string>
<string name="section_decrypt">"Decrypt / Verify"</string>
<string name="section_current_expiry">"Current expiry"</string>
@@ -84,12 +85,14 @@
<string name="btn_back">"Back"</string>
<string name="btn_no">"No"</string>
<string name="btn_match">"Fingerprints match"</string>
<string name="btn_match_phrases">"Phrases match"</string>
<string name="btn_share_encrypted_signed">"Encrypt/sign and share text"</string>
<string name="btn_copy_encrypted_signed">"Encrypt/sign and copy text"</string>
<string name="btn_view_cert_key">"View certification key"</string>
<string name="btn_create_key">"Create key"</string>
<string name="btn_add_files">"Add file(s)"</string>
<string name="btn_share_decrypted_text">"Share"</string>
<string name="btn_open_with">"Open with…"</string>
<string name="btn_copy_decrypted_text">"Copy decrypted text"</string>
<string name="btn_decrypt_clipboard">"Read from clipboard"</string>
<string name="btn_decrypt_files">"Select input file"</string>
@@ -100,6 +103,7 @@
<string name="btn_add_keyserver">"Add"</string>
<string name="btn_save_default">"Save as default"</string>
<string name="btn_saved">"Saved!"</string>
<string name="btn_not_matching">"Doesn't match"</string>
<!-- menu -->
<string name="menu_preferences">"Settings"</string>
@@ -116,7 +120,7 @@
<string name="menu_update_all_keys">"Update all keys"</string>
<string name="menu_advanced">"Extended information"</string>
<string name="menu_certify_fingerprint">"Confirm via fingerprint"</string>
<string name="menu_certify_fingerprint_word">"Confirm via words"</string>
<string name="menu_certify_fingerprint_phrases">"Confirm via phrases"</string>
<string name="menu_share_log">"Share Log"</string>
<string name="menu_keyserver_add">"Add"</string>
@@ -173,8 +177,9 @@
<string name="label_encrypt_filenames">"Encrypt filenames"</string>
<string name="label_hidden_recipients">"Hide recipients"</string>
<string name="label_verify_keyserver">"Verify keyserver"</string>
<string name="label_enter_keyserver_url">"Enter keyserver URL"</string>
<string name="label_verify_keyserver_connection">"Test connection"</string>
<string name="label_only_trusted_keyserver">"Only trusted keyserver"</string>
<string name="label_enter_keyserver_url">"URL"</string>
<string name="label_keyserver_dialog_delete">"Delete keyserver"</string>
<string name="label_theme">"Theme"</string>
@@ -195,8 +200,8 @@
<string name="label_experimental_settings_desc_title">"Warning"</string>
<string name="label_experimental_settings_desc_summary">"These features are not yet finished or results of user experience/security research. Thus, don't rely on their security and please don't report issues you encounter!"</string>
<string name="label_experimental_settings_word_confirm_title">"Word Confirm"</string>
<string name="label_experimental_settings_word_confirm_summary">"Confirm keys with words instead of hexadecimal fingerprints"</string>
<string name="label_experimental_settings_word_confirm_title">"Phrase Confirmation"</string>
<string name="label_experimental_settings_word_confirm_summary">"Confirm keys with phrases instead of hexadecimal fingerprints"</string>
<string name="label_experimental_settings_linked_identities_title">"Linked Identities"</string>
<string name="label_experimental_settings_linked_identities_summary">"Link keys to Twitter, GitHub, websites or DNS (similar to keybase.io but decentralized)"</string>
<string name="label_experimental_settings_keybase_title">"Keybase.io Proofs"</string>
@@ -446,7 +451,7 @@
<string name="progress_con_saving">"consolidate: saving to cache…"</string>
<string name="progress_con_reimport">"consolidate: reimporting…"</string>
<string name="progress_verifying_keyserver_url">"verifying keyserver…"</string>
<string name="progress_verifying_keyserver_connection">"verifying connection…"</string>
<string name="progress_starting_orbot">"Starting Orbot…"</string>
@@ -779,9 +784,10 @@
<!-- Add/Edit keyserver -->
<string name="add_keyserver_dialog_title">"Add keyserver"</string>
<string name="edit_keyserver_dialog_title">"Edit keyserver"</string>
<string name="add_keyserver_verified">"Keyserver verified!"</string>
<string name="add_keyserver_connection_verified">"Connection verified!"</string>
<string name="add_keyserver_without_verification">"Keyserver added without verification."</string>
<string name="add_keyserver_invalid_url">"Invalid URL!"</string>
<string name="add_keyserver_keyserver_not_trusted">"Keyserver is not one of the trusted ones (no pinned certificate available)!"</string>
<string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your Internet connection."</string>
<string name="keyserver_preference_deleted">"%s deleted"</string>
<string name="keyserver_preference_cannot_delete_last">"Cannot delete last keyserver. At least one is required!"</string>
@@ -990,6 +996,7 @@
<string name="msg_kc_uid_no_cert">"No valid self-certificate found for user ID '%s', removing from ring"</string>
<string name="msg_kc_uid_remove">"Removing invalid user ID '%s'"</string>
<string name="msg_kc_uid_dup">"Removing duplicate user ID '%s'. The keyring contained two of them. This may result in missing certificates!"</string>
<string name="msg_kc_uid_too_many">"Removing user ID '%s'. More than 100 User IDs are not imported!"</string>
<string name="msg_kc_uid_warn_encoding">"User ID does not verify as UTF-8!"</string>
<string name="msg_kc_uat_jpeg">"Processing user attribute of type JPEG"</string>
<string name="msg_kc_uat_unknown">"Processing user attribute of unknown type"</string>
@@ -1362,7 +1369,7 @@
<string name="msg_data_detached_unsupported">"Unsupported type of detached signature!"</string>
<string name="msg_data_error_io">"Error reading input data!"</string>
<string name="msg_data_error_openpgp">"Error processing OpenPGP data!"</string>
<string name="msg_data_mime_error">"Error parsing MIME data!"</string>
<string name="msg_data_mime_bad">"Could not parse as MIME data"</string>
<string name="msg_data_mime_filename">"Filename: '%s'"</string>
<string name="msg_data_mime_length">"Content-Length: %s"</string>
<string name="msg_data_mime">"Parsing MIME data structure"</string>
@@ -1441,9 +1448,8 @@
<string name="certs_text">"Only validated self-certificates and validated certificates created with your keys are displayed here."</string>
<string name="section_uids_to_certify">"Identities for "</string>
<string name="certify_text">"The keys you are importing contain “identities”: names and email addresses. Select exactly those for confirmation which match what you expected."</string>
<string name="certify_fingerprint_text">"Compare the displayed fingerprint, character by character, with the one displayed on your partners device."</string>
<string name="certify_fingerprint_text_words">"Compare the displayed fingerprint, word by word, with the one displayed on your partners device."</string>
<string name="certify_fingerprint_text2">"Do the fingerprints match?"</string>
<string name="certify_fingerprint_text">"Compare the fingerprint, character by character, with the one displayed on your partners device."</string>
<string name="certify_fingerprint_text_phrases">"Compare these phrases with the ones displayed on your partners device."</string>
<string name="label_revocation">"Revocation Reason"</string>
<string name="label_cert_type">"Type"</string>
<string name="error_key_not_found">"Key not found!"</string>