Merge remote-tracking branch 'origin/master' into encrypted-export
This commit is contained in:
@@ -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 -->
|
||||
|
||||
38
OpenKeychain/src/main/assets/api.keybase.io.CA.cer
Normal file
38
OpenKeychain/src/main/assets/api.keybase.io.CA.cer
Normal 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-----
|
||||
33
OpenKeychain/src/main/assets/pgp.mit.edu.cer
Normal file
33
OpenKeychain/src/main/assets/pgp.mit.edu.cer
Normal 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-----
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
));
|
||||
|
||||
@@ -77,7 +77,7 @@ public class CloudSearch {
|
||||
// kill threads that haven't returned yet
|
||||
thread.interrupt();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class DecryptActivity extends BaseActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
uris.add(intent.getData());
|
||||
uris.add(uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,8 +103,7 @@ public class EmailKeyHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Keyserver.QueryFailedException ignored) {
|
||||
} catch (Keyserver.QueryNeedsRepairException ignored) {
|
||||
} catch (Keyserver.CloudSearchFailureException ignored) {
|
||||
}
|
||||
return new ArrayList<>(keys);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_check_black_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_check_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 169 B |
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_check_black_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_check_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 B |
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_check_black_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_check_black_24dp.png
Normal file
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 |
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
19
OpenKeychain/src/main/res/menu-v19/decrypt_bottom_sheet.xml
Normal file
19
OpenKeychain/src/main/res/menu-v19/decrypt_bottom_sheet.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
128
OpenKeychain/src/main/res/raw/fp_sentence_adjectives
Normal file
128
OpenKeychain/src/main/res/raw/fp_sentence_adjectives
Normal 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
|
||||
64
OpenKeychain/src/main/res/raw/fp_sentence_adverbs
Normal file
64
OpenKeychain/src/main/res/raw/fp_sentence_adverbs
Normal 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
|
||||
8
OpenKeychain/src/main/res/raw/fp_sentence_articles
Normal file
8
OpenKeychain/src/main/res/raw/fp_sentence_articles
Normal file
@@ -0,0 +1,8 @@
|
||||
her
|
||||
his
|
||||
my
|
||||
our
|
||||
that
|
||||
the
|
||||
this
|
||||
your
|
||||
512
OpenKeychain/src/main/res/raw/fp_sentence_nouns
Normal file
512
OpenKeychain/src/main/res/raw/fp_sentence_nouns
Normal 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
|
||||
32
OpenKeychain/src/main/res/raw/fp_sentence_prepositions
Normal file
32
OpenKeychain/src/main/res/raw/fp_sentence_prepositions
Normal 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
|
||||
128
OpenKeychain/src/main/res/raw/fp_sentence_verbs_i
Normal file
128
OpenKeychain/src/main/res/raw/fp_sentence_verbs_i
Normal 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
|
||||
128
OpenKeychain/src/main/res/raw/fp_sentence_verbs_t
Normal file
128
OpenKeychain/src/main/res/raw/fp_sentence_verbs_t
Normal 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
|
||||
@@ -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"><žádný></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"><нет></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-->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"><ingen></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 partner’s device."</string>
|
||||
<string name="certify_fingerprint_text_phrases">"Compare these phrases with the ones displayed on your partner’s 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>
|
||||
|
||||
Reference in New Issue
Block a user