Merge branch 'master' into yubikey
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -60,10 +61,6 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
return mRing;
|
||||
}
|
||||
|
||||
public void encode(ArmoredOutputStream stream) throws IOException {
|
||||
getRing().encode(stream);
|
||||
}
|
||||
|
||||
/** Getter that returns the subkey that should be used for signing. */
|
||||
CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {
|
||||
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -523,13 +523,7 @@ public class PgpDecryptVerify {
|
||||
|
||||
// update signature buffer if signature is also present
|
||||
if (signature != null) {
|
||||
try {
|
||||
signature.update(buffer, 0, length);
|
||||
} catch (SignatureException e) {
|
||||
Log.e(Constants.TAG, "SignatureException -> Not a valid signature!", e);
|
||||
signatureResultBuilder.validSignature(false);
|
||||
signature = null;
|
||||
}
|
||||
signature.update(buffer, 0, length);
|
||||
}
|
||||
|
||||
alreadyWritten += length;
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -68,76 +67,6 @@ public class PgpHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// public static final class content {
|
||||
// public static final int unknown = 0;
|
||||
// public static final int encrypted_data = 1;
|
||||
// public static final int keys = 2;
|
||||
// }
|
||||
//
|
||||
// public static int getStreamContent(Context context, InputStream inStream) throws IOException {
|
||||
//
|
||||
// InputStream in = PGPUtil.getDecoderStream(inStream);
|
||||
// PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||
// Object object = pgpF.nextObject();
|
||||
// while (object != null) {
|
||||
// if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) {
|
||||
// return Id.content.keys;
|
||||
// } else if (object instanceof PGPEncryptedDataList) {
|
||||
// return Id.content.encrypted_data;
|
||||
// }
|
||||
// object = pgpF.nextObject();
|
||||
// }
|
||||
//
|
||||
// return Id.content.unknown;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Generate a random filename
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public static String generateRandomFilename(int length) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
|
||||
byte bytes[] = new byte[length];
|
||||
random.nextBytes(bytes);
|
||||
String result = "";
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int v = (bytes[i] + 256) % 64;
|
||||
if (v < 10) {
|
||||
result += (char) ('0' + v);
|
||||
} else if (v < 36) {
|
||||
result += (char) ('A' + v - 10);
|
||||
} else if (v < 62) {
|
||||
result += (char) ('a' + v - 36);
|
||||
} else if (v == 62) {
|
||||
result += '_';
|
||||
} else if (v == 63) {
|
||||
result += '.';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go once through stream to get length of stream. The length is later used to display progress
|
||||
* when encrypting/decrypting
|
||||
*
|
||||
* @param in
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static long getLengthOfStream(InputStream in) throws IOException {
|
||||
long size = 0;
|
||||
long n = 0;
|
||||
byte dummy[] = new byte[0x10000];
|
||||
while ((n = in.read(dummy)) > 0) {
|
||||
size += n;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes file securely by overwriting it with random data before deleting it.
|
||||
* <p/>
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
|
||||
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
@@ -43,6 +44,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class PgpImportExport {
|
||||
@@ -60,10 +62,14 @@ public class PgpImportExport {
|
||||
private ProviderHelper mProviderHelper;
|
||||
|
||||
public PgpImportExport(Context context, Progressable progressable) {
|
||||
this(context, new ProviderHelper(context), progressable);
|
||||
}
|
||||
|
||||
public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable) {
|
||||
super();
|
||||
this.mContext = context;
|
||||
this.mProgressable = progressable;
|
||||
this.mProviderHelper = new ProviderHelper(context);
|
||||
this.mProviderHelper = providerHelper;
|
||||
}
|
||||
|
||||
public PgpImportExport(Context context,
|
||||
@@ -93,7 +99,7 @@ public class PgpImportExport {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {
|
||||
public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ArmoredOutputStream aos = null;
|
||||
try {
|
||||
@@ -103,13 +109,9 @@ public class PgpImportExport {
|
||||
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
server.add(armoredKey);
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} catch (AddKeyException e) {
|
||||
// TODO: tell the user?
|
||||
return false;
|
||||
Log.e(Constants.TAG, "IOException", e);
|
||||
throw new AddKeyException();
|
||||
} finally {
|
||||
try {
|
||||
if (aos != null) {
|
||||
@@ -124,20 +126,23 @@ public class PgpImportExport {
|
||||
|
||||
/** Imports keys from given data. If keyIds is given only those are imported */
|
||||
public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {
|
||||
return importKeyRings(entries.iterator(), entries.size());
|
||||
}
|
||||
|
||||
public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num) {
|
||||
updateProgress(R.string.progress_importing, 0, 100);
|
||||
|
||||
// If there aren't even any keys, do nothing here.
|
||||
if (entries == null || entries.size() == 0) {
|
||||
if (entries == null || !entries.hasNext()) {
|
||||
return new ImportKeyResult(
|
||||
ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
|
||||
ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int newKeys = 0, oldKeys = 0, badKeys = 0;
|
||||
int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
|
||||
|
||||
int position = 0;
|
||||
int progSteps = 100 / entries.size();
|
||||
for (ParcelableKeyRing entry : entries) {
|
||||
double progSteps = 100.0 / num;
|
||||
for (ParcelableKeyRing entry : new IterableIterator<ParcelableKeyRing>(entries)) {
|
||||
try {
|
||||
UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes());
|
||||
|
||||
@@ -157,10 +162,10 @@ public class PgpImportExport {
|
||||
SaveKeyringResult result;
|
||||
if (key.isSecret()) {
|
||||
result = mProviderHelper.saveSecretKeyRing(key,
|
||||
new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100));
|
||||
new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
|
||||
} else {
|
||||
result = mProviderHelper.savePublicKeyRing(key,
|
||||
new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100));
|
||||
new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
|
||||
}
|
||||
if (!result.success()) {
|
||||
badKeys += 1;
|
||||
@@ -168,6 +173,9 @@ public class PgpImportExport {
|
||||
oldKeys += 1;
|
||||
} else {
|
||||
newKeys += 1;
|
||||
if (key.isSecret()) {
|
||||
secret += 1;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
@@ -204,7 +212,7 @@ public class PgpImportExport {
|
||||
}
|
||||
}
|
||||
|
||||
return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);
|
||||
return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,16 @@ import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.spongycastle.bcpg.ECPublicBCPGKey;
|
||||
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.security.DigestException;
|
||||
@@ -37,18 +43,14 @@ import java.util.Locale;
|
||||
|
||||
public class PgpKeyHelper {
|
||||
|
||||
public static String getAlgorithmInfo(int algorithm) {
|
||||
return getAlgorithmInfo(null, algorithm, 0);
|
||||
}
|
||||
|
||||
public static String getAlgorithmInfo(Context context, int algorithm) {
|
||||
return getAlgorithmInfo(context, algorithm, 0);
|
||||
public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) {
|
||||
return getAlgorithmInfo(null, algorithm, keySize, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
|
||||
*/
|
||||
public static String getAlgorithmInfo(Context context, int algorithm, int keySize) {
|
||||
public static String getAlgorithmInfo(Context context, int algorithm, Integer keySize, String oid) {
|
||||
String algorithmStr;
|
||||
|
||||
switch (algorithm) {
|
||||
@@ -69,10 +71,19 @@ public class PgpKeyHelper {
|
||||
break;
|
||||
}
|
||||
|
||||
case PublicKeyAlgorithmTags.ECDSA:
|
||||
case PublicKeyAlgorithmTags.ECDSA: {
|
||||
if (oid == null) {
|
||||
return "ECDSA";
|
||||
}
|
||||
String oidName = PgpKeyHelper.getCurveInfo(context, oid);
|
||||
return "ECDSA (" + oidName + ")";
|
||||
}
|
||||
case PublicKeyAlgorithmTags.ECDH: {
|
||||
algorithmStr = "ECC";
|
||||
break;
|
||||
if (oid == null) {
|
||||
return "ECDH";
|
||||
}
|
||||
String oidName = PgpKeyHelper.getCurveInfo(context, oid);
|
||||
return "ECDH (" + oidName + ")";
|
||||
}
|
||||
|
||||
default: {
|
||||
@@ -90,6 +101,106 @@ public class PgpKeyHelper {
|
||||
return algorithmStr;
|
||||
}
|
||||
|
||||
public static String getAlgorithmInfo(Algorithm algorithm, Integer keySize, Curve curve) {
|
||||
return getAlgorithmInfo(null, algorithm, keySize, curve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
|
||||
*/
|
||||
public static String getAlgorithmInfo(Context context, Algorithm algorithm, Integer keySize, Curve curve) {
|
||||
String algorithmStr;
|
||||
|
||||
switch (algorithm) {
|
||||
case RSA: {
|
||||
algorithmStr = "RSA";
|
||||
break;
|
||||
}
|
||||
case DSA: {
|
||||
algorithmStr = "DSA";
|
||||
break;
|
||||
}
|
||||
|
||||
case ELGAMAL: {
|
||||
algorithmStr = "ElGamal";
|
||||
break;
|
||||
}
|
||||
|
||||
case ECDSA: {
|
||||
algorithmStr = "ECDSA";
|
||||
if (curve != null) {
|
||||
algorithmStr += " (" + getCurveInfo(context, curve) + ")";
|
||||
}
|
||||
return algorithmStr;
|
||||
}
|
||||
case ECDH: {
|
||||
algorithmStr = "ECDH";
|
||||
if (curve != null) {
|
||||
algorithmStr += " (" + getCurveInfo(context, curve) + ")";
|
||||
}
|
||||
return algorithmStr;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (context != null) {
|
||||
algorithmStr = context.getResources().getString(R.string.unknown_algorithm);
|
||||
} else {
|
||||
algorithmStr = "unknown";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keySize != null && keySize > 0)
|
||||
return algorithmStr + ", " + keySize + " bit";
|
||||
else
|
||||
return algorithmStr;
|
||||
}
|
||||
|
||||
// Return name of a curve. These are names, no need for translation
|
||||
public static String getCurveInfo(Context context, Curve curve) {
|
||||
switch(curve) {
|
||||
case NIST_P256:
|
||||
return "NIST P-256";
|
||||
case NIST_P384:
|
||||
return "NIST P-384";
|
||||
case NIST_P521:
|
||||
return "NIST P-521";
|
||||
|
||||
/* see SaveKeyringParcel
|
||||
case BRAINPOOL_P256:
|
||||
return "Brainpool P-256";
|
||||
case BRAINPOOL_P384:
|
||||
return "Brainpool P-384";
|
||||
case BRAINPOOL_P512:
|
||||
return "Brainpool P-512";
|
||||
*/
|
||||
}
|
||||
if (context != null) {
|
||||
return context.getResources().getString(R.string.unknown_algorithm);
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCurveInfo(Context context, String oidStr) {
|
||||
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(oidStr);
|
||||
|
||||
String name;
|
||||
name = NISTNamedCurves.getName(oid);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
name = TeleTrusTNamedCurves.getName(oid);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
if (context != null) {
|
||||
return context.getResources().getString(R.string.unknown_algorithm);
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts fingerprint to hex
|
||||
* <p/>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -70,7 +70,7 @@ public class PgpSignEncrypt {
|
||||
private long mSignatureMasterKeyId;
|
||||
private int mSignatureHashAlgorithm;
|
||||
private String mSignaturePassphrase;
|
||||
private boolean mEncryptToSigner;
|
||||
private long mAdditionalEncryptId;
|
||||
private boolean mCleartextInput;
|
||||
private String mOriginalFilename;
|
||||
|
||||
@@ -103,7 +103,7 @@ public class PgpSignEncrypt {
|
||||
this.mSignatureMasterKeyId = builder.mSignatureMasterKeyId;
|
||||
this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
|
||||
this.mSignaturePassphrase = builder.mSignaturePassphrase;
|
||||
this.mEncryptToSigner = builder.mEncryptToSigner;
|
||||
this.mAdditionalEncryptId = builder.mAdditionalEncryptId;
|
||||
this.mCleartextInput = builder.mCleartextInput;
|
||||
this.mNfcSignedHash = builder.mNfcSignedHash;
|
||||
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
|
||||
@@ -127,7 +127,7 @@ public class PgpSignEncrypt {
|
||||
private long mSignatureMasterKeyId = Constants.key.none;
|
||||
private int mSignatureHashAlgorithm = 0;
|
||||
private String mSignaturePassphrase = null;
|
||||
private boolean mEncryptToSigner = false;
|
||||
private long mAdditionalEncryptId = Constants.key.none;
|
||||
private boolean mCleartextInput = false;
|
||||
private String mOriginalFilename = "";
|
||||
private byte[] mNfcSignedHash = null;
|
||||
@@ -175,7 +175,7 @@ public class PgpSignEncrypt {
|
||||
}
|
||||
|
||||
public Builder setSignatureMasterKeyId(long signatureMasterKeyId) {
|
||||
this.mSignatureMasterKeyId = signatureMasterKeyId;
|
||||
mSignatureMasterKeyId = signatureMasterKeyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -192,11 +192,11 @@ public class PgpSignEncrypt {
|
||||
/**
|
||||
* Also encrypt with the signing keyring
|
||||
*
|
||||
* @param encryptToSigner
|
||||
* @param additionalEncryptId
|
||||
* @return
|
||||
*/
|
||||
public Builder setEncryptToSigner(boolean encryptToSigner) {
|
||||
mEncryptToSigner = encryptToSigner;
|
||||
public Builder setAdditionalEncryptId(long additionalEncryptId) {
|
||||
mAdditionalEncryptId = additionalEncryptId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -288,10 +288,10 @@ public class PgpSignEncrypt {
|
||||
+ "\nenableCompression:" + enableCompression
|
||||
+ "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput);
|
||||
|
||||
// add signature key id to encryption ids (self-encrypt)
|
||||
if (enableEncryption && enableSignature && mEncryptToSigner) {
|
||||
// add additional key id to encryption ids (mostly to do self-encryption)
|
||||
if (enableEncryption && mAdditionalEncryptId != Constants.key.none) {
|
||||
mEncryptionMasterKeyIds = Arrays.copyOf(mEncryptionMasterKeyIds, mEncryptionMasterKeyIds.length + 1);
|
||||
mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId;
|
||||
mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mAdditionalEncryptId;
|
||||
}
|
||||
|
||||
ArmoredOutputStream armorOut = null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -44,6 +45,8 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
@@ -55,7 +58,8 @@ import java.util.TreeSet;
|
||||
* This class and its relatives UncachedPublicKey and UncachedSecretKey are
|
||||
* used to move around pgp key rings in non crypto related (UI, mostly) code.
|
||||
* It should be used for simple inspection only until it saved in the database,
|
||||
* all actual crypto operations should work with WrappedKeyRings exclusively.
|
||||
* all actual crypto operations should work with CanonicalizedKeyRings
|
||||
* exclusively.
|
||||
*
|
||||
* This class is also special in that it can hold either the PGPPublicKeyRing
|
||||
* or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
|
||||
@@ -118,6 +122,10 @@ public class UncachedKeyRing {
|
||||
return mRing.getPublicKey().getFingerprint();
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return mRing.getPublicKey().getVersion();
|
||||
}
|
||||
|
||||
public static UncachedKeyRing decodeFromData(byte[] data)
|
||||
throws PgpGeneralException, IOException {
|
||||
|
||||
@@ -211,8 +219,7 @@ public class UncachedKeyRing {
|
||||
aos.close();
|
||||
}
|
||||
|
||||
/** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be
|
||||
* applied to public keyrings only.
|
||||
/** "Canonicalizes" a public key, removing inconsistencies in the process.
|
||||
*
|
||||
* More specifically:
|
||||
* - Remove all non-verifying self-certificates
|
||||
@@ -229,9 +236,9 @@ public class UncachedKeyRing {
|
||||
* - If the key is a secret key, remove all certificates by foreign keys
|
||||
* - If no valid user id remains, log an error and return null
|
||||
*
|
||||
* This operation writes an OperationLog which can be used as part of a OperationResultParcel.
|
||||
* This operation writes an OperationLog which can be used as part of an OperationResultParcel.
|
||||
*
|
||||
* @return A canonicalized key, or null on fatal error
|
||||
* @return A canonicalized key, or null on fatal error (log will include a message in this case)
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@@ -241,6 +248,12 @@ public class UncachedKeyRing {
|
||||
indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()));
|
||||
indent += 1;
|
||||
|
||||
// do not accept v3 keys
|
||||
if (getVersion() <= 3) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_KC_V3_KEY, indent);
|
||||
return null;
|
||||
}
|
||||
|
||||
final Date now = new Date();
|
||||
|
||||
int redundantCerts = 0, badCerts = 0;
|
||||
@@ -259,13 +272,12 @@ public class UncachedKeyRing {
|
||||
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
|
||||
int type = zert.getSignatureType();
|
||||
|
||||
// Disregard certifications on user ids, we will deal with those later
|
||||
// These should most definitely not be here...
|
||||
if (type == PGPSignature.NO_CERTIFICATION
|
||||
|| type == PGPSignature.DEFAULT_CERTIFICATION
|
||||
|| type == PGPSignature.CASUAL_CERTIFICATION
|
||||
|| type == PGPSignature.POSITIVE_CERTIFICATION
|
||||
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
|
||||
// These should not be here...
|
||||
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
badCerts += 1;
|
||||
@@ -328,7 +340,17 @@ public class UncachedKeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> processedUserIds = new ArrayList<String>();
|
||||
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
|
||||
// check for duplicate user ids
|
||||
if (processedUserIds.contains(userId)) {
|
||||
log.add(LogLevel.WARN, LogType.MSG_KC_UID_DUP,
|
||||
indent, userId);
|
||||
// strip out the first found user id with this name
|
||||
modified = PGPPublicKey.removeCertification(modified, userId);
|
||||
}
|
||||
processedUserIds.add(userId);
|
||||
|
||||
PGPSignature selfCert = null;
|
||||
revocation = null;
|
||||
|
||||
@@ -405,13 +427,13 @@ public class UncachedKeyRing {
|
||||
if (selfCert == null) {
|
||||
selfCert = zert;
|
||||
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
|
||||
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
|
||||
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
|
||||
indent, userId);
|
||||
modified = PGPPublicKey.removeCertification(modified, userId, selfCert);
|
||||
redundantCerts += 1;
|
||||
selfCert = zert;
|
||||
} else {
|
||||
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
|
||||
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
|
||||
indent, userId);
|
||||
modified = PGPPublicKey.removeCertification(modified, userId, zert);
|
||||
redundantCerts += 1;
|
||||
@@ -474,6 +496,10 @@ public class UncachedKeyRing {
|
||||
|
||||
// Replace modified key in the keyring
|
||||
ring = replacePublicKey(ring, modified);
|
||||
if (ring == null) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
|
||||
return null;
|
||||
}
|
||||
indent -= 1;
|
||||
|
||||
}
|
||||
@@ -580,8 +606,8 @@ public class UncachedKeyRing {
|
||||
|
||||
}
|
||||
|
||||
// if we already have a cert, and this one is not newer: skip it
|
||||
if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) {
|
||||
// if we already have a cert, and this one is older: skip it
|
||||
if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) {
|
||||
log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent);
|
||||
redundantCerts += 1;
|
||||
continue;
|
||||
@@ -641,6 +667,10 @@ public class UncachedKeyRing {
|
||||
}
|
||||
// replace pubkey in keyring
|
||||
ring = replacePublicKey(ring, modified);
|
||||
if (ring == null) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
|
||||
return null;
|
||||
}
|
||||
indent -= 1;
|
||||
}
|
||||
|
||||
@@ -681,8 +711,9 @@ public class UncachedKeyRing {
|
||||
|
||||
long masterKeyId = other.getMasterKeyId();
|
||||
|
||||
if (getMasterKeyId() != masterKeyId) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_HETEROGENEOUS, indent);
|
||||
if (getMasterKeyId() != masterKeyId
|
||||
|| !Arrays.equals(getFingerprint(), other.getFingerprint())) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_HETEROGENEOUS, indent);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -729,6 +760,10 @@ public class UncachedKeyRing {
|
||||
} else {
|
||||
// otherwise, just insert the public key
|
||||
result = replacePublicKey(result, key);
|
||||
if (result == null) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -757,6 +792,10 @@ public class UncachedKeyRing {
|
||||
if (!key.isMasterKey()) {
|
||||
if (modified != resultKey) {
|
||||
result = replacePublicKey(result, modified);
|
||||
if (result == null) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -781,6 +820,10 @@ public class UncachedKeyRing {
|
||||
// If anything changed, save the updated (sub)key
|
||||
if (modified != resultKey) {
|
||||
result = replacePublicKey(result, modified);
|
||||
if (result == null) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -795,7 +838,7 @@ public class UncachedKeyRing {
|
||||
return new UncachedKeyRing(result);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_FATAL_ENCODE, indent);
|
||||
log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_ENCODE, indent);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -826,16 +869,19 @@ public class UncachedKeyRing {
|
||||
*/
|
||||
private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) {
|
||||
if (ring instanceof PGPPublicKeyRing) {
|
||||
return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key);
|
||||
PGPPublicKeyRing pubRing = (PGPPublicKeyRing) ring;
|
||||
return PGPPublicKeyRing.insertPublicKey(pubRing, key);
|
||||
} else {
|
||||
PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring;
|
||||
PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID());
|
||||
// TODO generate secret key with S2K dummy, if none exists!
|
||||
if (sKey == null) {
|
||||
Log.e(Constants.TAG, "dummy secret key generation not yet implemented");
|
||||
return null;
|
||||
}
|
||||
sKey = PGPSecretKey.replacePublicKey(sKey, key);
|
||||
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
|
||||
}
|
||||
PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring;
|
||||
PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID());
|
||||
// TODO generate secret key with S2K dummy, if none exists! for now, just die.
|
||||
if (sKey == null) {
|
||||
throw new RuntimeException("dummy secret key generation not yet implemented");
|
||||
}
|
||||
sKey = PGPSecretKey.replacePublicKey(sKey, key);
|
||||
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
|
||||
}
|
||||
|
||||
/** This method removes a subkey in a keyring.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,6 +18,10 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.spongycastle.bcpg.ECPublicBCPGKey;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
@@ -93,10 +98,23 @@ public class UncachedPublicKey {
|
||||
return mPublicKey.getAlgorithm();
|
||||
}
|
||||
|
||||
public int getBitStrength() {
|
||||
public Integer getBitStrength() {
|
||||
if (isEC()) {
|
||||
return null;
|
||||
}
|
||||
return mPublicKey.getBitStrength();
|
||||
}
|
||||
|
||||
public String getCurveOid() {
|
||||
if ( ! isEC()) {
|
||||
return null;
|
||||
}
|
||||
if ( ! (mPublicKey.getPublicKeyPacket().getKey() instanceof ECPublicBCPGKey)) {
|
||||
return null;
|
||||
}
|
||||
return ((ECPublicBCPGKey) mPublicKey.getPublicKeyPacket().getKey()).getCurveOID().getId();
|
||||
}
|
||||
|
||||
/** Returns the primary user id, as indicated by the public key's self certificates.
|
||||
*
|
||||
* This is an expensive operation, since potentially a lot of certificates (and revocations)
|
||||
@@ -185,6 +203,10 @@ public class UncachedPublicKey {
|
||||
return getAlgorithm() == PGPPublicKey.DSA;
|
||||
}
|
||||
|
||||
public boolean isEC() {
|
||||
return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// TODO make this safe
|
||||
public int getKeyUsage() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -95,9 +96,6 @@ public class WrappedSignature {
|
||||
} catch (PGPException e) {
|
||||
// no matter
|
||||
Log.e(Constants.TAG, "exception reading embedded signatures", e);
|
||||
} catch (IOException e) {
|
||||
// no matter
|
||||
Log.e(Constants.TAG, "exception reading embedded signatures", e);
|
||||
}
|
||||
return sigs;
|
||||
}
|
||||
@@ -149,27 +147,17 @@ public class WrappedSignature {
|
||||
}
|
||||
}
|
||||
|
||||
public void update(byte[] data, int offset, int length) throws PgpGeneralException {
|
||||
try {
|
||||
mSig.update(data, offset, length);
|
||||
} catch(SignatureException e) {
|
||||
throw new PgpGeneralException(e);
|
||||
}
|
||||
public void update(byte[] data, int offset, int length) {
|
||||
mSig.update(data, offset, length);
|
||||
}
|
||||
|
||||
public void update(byte data) throws PgpGeneralException {
|
||||
try {
|
||||
mSig.update(data);
|
||||
} catch(SignatureException e) {
|
||||
throw new PgpGeneralException(e);
|
||||
}
|
||||
public void update(byte data) {
|
||||
mSig.update(data);
|
||||
}
|
||||
|
||||
public boolean verify() throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verify();
|
||||
} catch(SignatureException e) {
|
||||
throw new PgpGeneralException(e);
|
||||
} catch(PGPException e) {
|
||||
throw new PgpGeneralException(e);
|
||||
}
|
||||
@@ -178,8 +166,6 @@ public class WrappedSignature {
|
||||
boolean verifySignature(PGPPublicKey key) throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verifyCertification(key);
|
||||
} catch (SignatureException e) {
|
||||
throw new PgpGeneralException("Sign!", e);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error!", e);
|
||||
}
|
||||
@@ -188,8 +174,6 @@ public class WrappedSignature {
|
||||
boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verifyCertification(masterKey, subKey);
|
||||
} catch (SignatureException e) {
|
||||
throw new PgpGeneralException("Sign!", e);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error!", e);
|
||||
}
|
||||
@@ -198,8 +182,6 @@ public class WrappedSignature {
|
||||
boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verifyCertification(uid, key);
|
||||
} catch (SignatureException e) {
|
||||
throw new PgpGeneralException("Error!", e);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error!", e);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
Reference in New Issue
Block a user