Merge branch 'development' into detached-sigs-api
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
|
||||
public boolean canSign() {
|
||||
// if key flags subpacket is available, honor it!
|
||||
if (getKeyUsage() != null) {
|
||||
if (getKeyUsage() != 0) {
|
||||
return (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
|
||||
public boolean canCertify() {
|
||||
// if key flags subpacket is available, honor it!
|
||||
if (getKeyUsage() != null) {
|
||||
if (getKeyUsage() != 0) {
|
||||
return (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
|
||||
public boolean canEncrypt() {
|
||||
// if key flags subpacket is available, honor it!
|
||||
if (getKeyUsage() != null) {
|
||||
if (getKeyUsage() != 0) {
|
||||
return (getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
|
||||
public boolean canAuthenticate() {
|
||||
// if key flags subpacket is available, honor it!
|
||||
if (getKeyUsage() != null) {
|
||||
if (getKeyUsage() != 0) {
|
||||
return (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
|
||||
@@ -76,7 +78,7 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
|
||||
return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
|
||||
return new IterableIterator<>(new Iterator<CanonicalizedPublicKey>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
@@ -94,4 +96,13 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
});
|
||||
}
|
||||
|
||||
/** Create a dummy secret ring from this key */
|
||||
public UncachedKeyRing createDummySecretRing () {
|
||||
|
||||
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(),
|
||||
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY);
|
||||
return new UncachedKeyRing(secRing);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -182,7 +182,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
* @return
|
||||
*/
|
||||
public LinkedList<Integer> getSupportedHashAlgorithms() {
|
||||
LinkedList<Integer> supported = new LinkedList<Integer>();
|
||||
LinkedList<Integer> supported = new LinkedList<>();
|
||||
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||
// No support for MD5
|
||||
@@ -262,11 +262,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
||||
signatureGenerator.setHashedSubpackets(spGen.generate());
|
||||
return signatureGenerator;
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
} catch (PgpKeyNotFoundException | PGPException e) {
|
||||
// TODO: simply throw PGPException!
|
||||
throw new PgpGeneralException("Error initializing signature!", e);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error initializing signature!", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,27 +18,19 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
@@ -94,7 +86,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
|
||||
public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
|
||||
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
|
||||
return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {
|
||||
return new IterableIterator<>(new Iterator<CanonicalizedSecretKey>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
@@ -114,7 +106,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
|
||||
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
|
||||
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
|
||||
return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
|
||||
return new IterableIterator<>(new Iterator<CanonicalizedPublicKey>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
@@ -133,7 +125,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
|
||||
}
|
||||
|
||||
public HashMap<String,String> getLocalNotationData() {
|
||||
HashMap<String,String> result = new HashMap<String,String>();
|
||||
HashMap<String,String> result = new HashMap<>();
|
||||
Iterator<PGPSignature> it = getRing().getPublicKey().getKeySignatures();
|
||||
while (it.hasNext()) {
|
||||
WrappedSignature sig = new WrappedSignature(it.next());
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@@ -33,7 +32,7 @@ public class OpenPgpSignatureResultBuilder {
|
||||
// OpenPgpSignatureResult
|
||||
private boolean mSignatureOnly = false;
|
||||
private String mPrimaryUserId;
|
||||
private ArrayList<String> mUserIds = new ArrayList<String>();
|
||||
private ArrayList<String> mUserIds = new ArrayList<>();
|
||||
private long mKeyId;
|
||||
|
||||
// builder
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
@@ -134,7 +135,7 @@ public class PgpKeyOperation {
|
||||
public PgpKeyOperation(Progressable progress) {
|
||||
super();
|
||||
if (progress != null) {
|
||||
mProgress = new Stack<Progressable>();
|
||||
mProgress = new Stack<>();
|
||||
mProgress.push(progress);
|
||||
}
|
||||
}
|
||||
@@ -287,13 +288,11 @@ public class PgpKeyOperation {
|
||||
// build new key pair
|
||||
return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
|
||||
|
||||
} catch(NoSuchProviderException e) {
|
||||
} catch(NoSuchProviderException | InvalidAlgorithmParameterException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch(NoSuchAlgorithmException e) {
|
||||
log.add(LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
|
||||
return null;
|
||||
} catch(InvalidAlgorithmParameterException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch(PGPException e) {
|
||||
Log.e(Constants.TAG, "internal pgp error", e);
|
||||
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
|
||||
@@ -388,6 +387,9 @@ public class PgpKeyOperation {
|
||||
* with a passphrase fails, the operation will fail with an unlocking error. More specific
|
||||
* handling of errors should be done in UI code!
|
||||
*
|
||||
* If the passphrase is null, only a restricted subset of operations will be available,
|
||||
* namely stripping of subkeys and changing the protection mode of dummy keys.
|
||||
*
|
||||
*/
|
||||
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
|
||||
String passphrase) {
|
||||
@@ -428,6 +430,11 @@ public class PgpKeyOperation {
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
// If we have no passphrase, only allow restricted operation
|
||||
if (passphrase == null) {
|
||||
return internalRestricted(sKR, saveParcel, log);
|
||||
}
|
||||
|
||||
// read masterKeyFlags, and use the same as before.
|
||||
// since this is the master key, this contains at least CERTIFY_OTHER
|
||||
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
|
||||
@@ -478,7 +485,7 @@ public class PgpKeyOperation {
|
||||
PGPPublicKey modifiedPublicKey = masterPublicKey;
|
||||
|
||||
// 2a. Add certificates for new user ids
|
||||
subProgressPush(15, 25);
|
||||
subProgressPush(15, 23);
|
||||
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
|
||||
|
||||
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
|
||||
@@ -495,7 +502,7 @@ public class PgpKeyOperation {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
|
||||
if (it != null) {
|
||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
|
||||
for (PGPSignature cert : new IterableIterator<>(it)) {
|
||||
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
||||
// foreign certificate?! error error error
|
||||
log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||
@@ -522,8 +529,37 @@ public class PgpKeyOperation {
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
// 2b. Add revocations for revoked user ids
|
||||
subProgressPush(25, 40);
|
||||
// 2b. Add certificates for new user ids
|
||||
subProgressPush(23, 32);
|
||||
for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) {
|
||||
|
||||
progress(R.string.progress_modify_adduat, (i - 1) * (100 / saveParcel.mAddUserAttribute.size()));
|
||||
WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i);
|
||||
|
||||
switch (attribute.getType()) {
|
||||
// the 'none' type must not succeed
|
||||
case WrappedUserAttribute.UAT_NONE:
|
||||
log.add(LogType.MSG_MF_UAT_ERROR_EMPTY, indent);
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
case WrappedUserAttribute.UAT_IMAGE:
|
||||
log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent);
|
||||
break;
|
||||
default:
|
||||
log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent);
|
||||
break;
|
||||
}
|
||||
|
||||
PGPUserAttributeSubpacketVector vector = attribute.getVector();
|
||||
|
||||
// generate and add new certificate
|
||||
PGPSignature cert = generateUserAttributeSignature(masterPrivateKey,
|
||||
masterPublicKey, vector);
|
||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert);
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
// 2c. Add revocations for revoked user ids
|
||||
subProgressPush(32, 40);
|
||||
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
|
||||
|
||||
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
|
||||
@@ -685,6 +721,27 @@ public class PgpKeyOperation {
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
if (change.mDummyStrip || change.mDummyDivert != null) {
|
||||
// IT'S DANGEROUS~
|
||||
// no really, it is. this operation irrevocably removes the private key data from the key
|
||||
if (change.mDummyStrip) {
|
||||
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
|
||||
} else {
|
||||
// the serial number must be 16 bytes in length
|
||||
if (change.mDummyDivert.length != 16) {
|
||||
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
|
||||
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
}
|
||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
|
||||
}
|
||||
|
||||
// This doesn't concern us any further
|
||||
if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// expiry must not be in the past
|
||||
if (change.mExpiry != null && change.mExpiry != 0 &&
|
||||
new Date(change.mExpiry*1000).before(new Date())) {
|
||||
@@ -775,30 +832,6 @@ public class PgpKeyOperation {
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
// 4c. For each subkey to be stripped... do so
|
||||
subProgressPush(65, 70);
|
||||
for (int i = 0; i < saveParcel.mStripSubKeys.size(); i++) {
|
||||
|
||||
progress(R.string.progress_modify_subkeystrip, (i-1) * (100 / saveParcel.mStripSubKeys.size()));
|
||||
long strip = saveParcel.mStripSubKeys.get(i);
|
||||
log.add(LogType.MSG_MF_SUBKEY_STRIP,
|
||||
indent, KeyFormattingUtils.convertKeyIdToHex(strip));
|
||||
|
||||
PGPSecretKey sKey = sKR.getSecretKey(strip);
|
||||
if (sKey == null) {
|
||||
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
|
||||
indent+1, KeyFormattingUtils.convertKeyIdToHex(strip));
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
// IT'S DANGEROUS~
|
||||
// no really, it is. this operation irrevocably removes the private key data from the key
|
||||
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
|
||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
|
||||
|
||||
}
|
||||
subProgressPop();
|
||||
|
||||
// 5. Generate and add new subkeys
|
||||
subProgressPush(70, 90);
|
||||
for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
|
||||
@@ -907,6 +940,73 @@ public class PgpKeyOperation {
|
||||
|
||||
}
|
||||
|
||||
/** This method does the actual modifications in a keyring just like internal, except it
|
||||
* supports only the subset of operations which require no passphrase, and will error
|
||||
* otherwise.
|
||||
*/
|
||||
private PgpEditKeyResult internalRestricted(PGPSecretKeyRing sKR, SaveKeyringParcel saveParcel,
|
||||
OperationLog log) {
|
||||
|
||||
int indent = 1;
|
||||
|
||||
progress(R.string.progress_modify, 0);
|
||||
|
||||
// Make sure the saveParcel includes only operations available without passphrae!
|
||||
if (!saveParcel.isRestrictedOnly()) {
|
||||
log.add(LogType.MSG_MF_ERROR_RESTRICTED, indent);
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
// Check if we were cancelled
|
||||
if (checkCancelled()) {
|
||||
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
|
||||
}
|
||||
|
||||
// The only operation we can do here:
|
||||
// 4a. Strip secret keys, or change their protection mode (stripped/divert-to-card)
|
||||
subProgressPush(50, 60);
|
||||
for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
|
||||
|
||||
progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / saveParcel.mChangeSubKeys.size()));
|
||||
SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
|
||||
log.add(LogType.MSG_MF_SUBKEY_CHANGE,
|
||||
indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
|
||||
|
||||
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
|
||||
if (sKey == null) {
|
||||
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
|
||||
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
|
||||
if (change.mDummyStrip || change.mDummyDivert != null) {
|
||||
// IT'S DANGEROUS~
|
||||
// no really, it is. this operation irrevocably removes the private key data from the key
|
||||
if (change.mDummyStrip) {
|
||||
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
|
||||
} else {
|
||||
// the serial number must be 16 bytes in length
|
||||
if (change.mDummyDivert.length != 16) {
|
||||
log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
|
||||
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
|
||||
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||
}
|
||||
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
|
||||
}
|
||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// And we're done!
|
||||
progress(R.string.progress_done, 100);
|
||||
log.add(LogType.MSG_MF_SUCCESS, indent);
|
||||
return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static PGPSecretKeyRing applyNewUnlock(
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
@@ -1174,6 +1274,26 @@ public class PgpKeyOperation {
|
||||
return sGen.generateCertification(userId, pKey);
|
||||
}
|
||||
|
||||
private static PGPSignature generateUserAttributeSignature(
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey,
|
||||
PGPUserAttributeSubpacketVector vector)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
|
||||
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
{
|
||||
/* critical subpackets: we consider those important for a modern pgp implementation */
|
||||
hashedPacketsGen.setSignatureCreationTime(true, new Date());
|
||||
}
|
||||
|
||||
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
||||
return sGen.generateCertification(vector, pKey);
|
||||
}
|
||||
|
||||
private static PGPSignature generateRevocationSignature(
|
||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.bcpg.UserAttributeSubpacketTags;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
@@ -30,6 +31,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureList;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
@@ -443,7 +445,7 @@ public class UncachedKeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> processedUserIds = new ArrayList<String>();
|
||||
ArrayList<String> processedUserIds = new ArrayList<>();
|
||||
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
|
||||
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
|
||||
|
||||
@@ -468,7 +470,7 @@ public class UncachedKeyRing {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(rawUserId);
|
||||
if (signaturesIt != null) {
|
||||
for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) {
|
||||
for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
|
||||
WrappedSignature cert = new WrappedSignature(zert);
|
||||
long certId = cert.getKeyId();
|
||||
|
||||
@@ -605,6 +607,170 @@ public class UncachedKeyRing {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList<PGPUserAttributeSubpacketVector> processedUserAttributes = new ArrayList<>();
|
||||
for (PGPUserAttributeSubpacketVector userAttribute :
|
||||
new IterableIterator<PGPUserAttributeSubpacketVector>(masterKey.getUserAttributes())) {
|
||||
|
||||
if (userAttribute.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) {
|
||||
log.add(LogType.MSG_KC_UAT_JPEG, indent);
|
||||
} else {
|
||||
log.add(LogType.MSG_KC_UAT_UNKNOWN, indent);
|
||||
}
|
||||
|
||||
try {
|
||||
indent += 1;
|
||||
|
||||
// check for duplicate user attributes
|
||||
if (processedUserAttributes.contains(userAttribute)) {
|
||||
log.add(LogType.MSG_KC_UAT_DUP, indent);
|
||||
// strip out the first found user id with this name
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute);
|
||||
}
|
||||
processedUserAttributes.add(userAttribute);
|
||||
|
||||
PGPSignature selfCert = null;
|
||||
revocation = null;
|
||||
|
||||
// look through signatures for this specific user id
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute);
|
||||
if (signaturesIt != null) {
|
||||
for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
|
||||
WrappedSignature cert = new WrappedSignature(zert);
|
||||
long certId = cert.getKeyId();
|
||||
|
||||
int type = zert.getSignatureType();
|
||||
if (type != PGPSignature.DEFAULT_CERTIFICATION
|
||||
&& type != PGPSignature.NO_CERTIFICATION
|
||||
&& type != PGPSignature.CASUAL_CERTIFICATION
|
||||
&& type != PGPSignature.POSITIVE_CERTIFICATION
|
||||
&& type != PGPSignature.CERTIFICATION_REVOCATION) {
|
||||
log.add(LogType.MSG_KC_UAT_BAD_TYPE,
|
||||
indent, "0x" + Integer.toString(zert.getSignatureType(), 16));
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cert.getCreationTime().after(nowPlusOneDay)) {
|
||||
// Creation date in the future? No way!
|
||||
log.add(LogType.MSG_KC_UAT_BAD_TIME, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cert.isLocal()) {
|
||||
// Creation date in the future? No way!
|
||||
log.add(LogType.MSG_KC_UAT_BAD_LOCAL, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a foreign signature, ...
|
||||
if (certId != masterKeyId) {
|
||||
// never mind any further for public keys, but remove them from secret ones
|
||||
if (isSecret()) {
|
||||
log.add(LogType.MSG_KC_UAT_FOREIGN,
|
||||
indent, KeyFormattingUtils.convertKeyIdToHex(certId));
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, first make sure it checks out
|
||||
try {
|
||||
cert.init(masterKey);
|
||||
if (!cert.verifySignature(masterKey, userAttribute)) {
|
||||
log.add(LogType.MSG_KC_UAT_BAD,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
} catch (PgpGeneralException e) {
|
||||
log.add(LogType.MSG_KC_UAT_BAD_ERR,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PGPSignature.DEFAULT_CERTIFICATION:
|
||||
case PGPSignature.NO_CERTIFICATION:
|
||||
case PGPSignature.CASUAL_CERTIFICATION:
|
||||
case PGPSignature.POSITIVE_CERTIFICATION:
|
||||
if (selfCert == null) {
|
||||
selfCert = zert;
|
||||
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
|
||||
log.add(LogType.MSG_KC_UAT_CERT_DUP,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, selfCert);
|
||||
redundantCerts += 1;
|
||||
selfCert = zert;
|
||||
} else {
|
||||
log.add(LogType.MSG_KC_UAT_CERT_DUP,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
redundantCerts += 1;
|
||||
}
|
||||
// If there is a revocation certificate, and it's older than this, drop it
|
||||
if (revocation != null
|
||||
&& revocation.getCreationTime().before(selfCert.getCreationTime())) {
|
||||
log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
|
||||
revocation = null;
|
||||
redundantCerts += 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case PGPSignature.CERTIFICATION_REVOCATION:
|
||||
// If this is older than the (latest) self cert, drop it
|
||||
if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
|
||||
log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
redundantCerts += 1;
|
||||
continue;
|
||||
}
|
||||
// first revocation? remember it.
|
||||
if (revocation == null) {
|
||||
revocation = zert;
|
||||
// more revocations? at least one is superfluous, then.
|
||||
} else if (revocation.getCreationTime().before(cert.getCreationTime())) {
|
||||
log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
|
||||
redundantCerts += 1;
|
||||
revocation = zert;
|
||||
} else {
|
||||
log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
|
||||
redundantCerts += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no valid certificate (if only a revocation) remains, drop it
|
||||
if (selfCert == null && revocation == null) {
|
||||
log.add(LogType.MSG_KC_UAT_REMOVE,
|
||||
indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, userAttribute);
|
||||
}
|
||||
|
||||
} finally {
|
||||
indent -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Replace modified key in the keyring
|
||||
ring = replacePublicKey(ring, modified);
|
||||
indent -= 1;
|
||||
@@ -612,7 +778,7 @@ public class UncachedKeyRing {
|
||||
}
|
||||
|
||||
// Keep track of ids we encountered so far
|
||||
Set<Long> knownIds = new HashSet<Long>();
|
||||
Set<Long> knownIds = new HashSet<>();
|
||||
|
||||
// Process all keys
|
||||
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(ring.getPublicKeys())) {
|
||||
@@ -852,8 +1018,8 @@ public class UncachedKeyRing {
|
||||
/** This operation merges information from a different keyring, returning a combined
|
||||
* UncachedKeyRing.
|
||||
*
|
||||
* The combined keyring contains the subkeys and user ids of both input keyrings, but it does
|
||||
* not necessarily have the canonicalized property.
|
||||
* The combined keyring contains the subkeys, user ids and user attributes of both input
|
||||
* keyrings, but it does not necessarily have the canonicalized property.
|
||||
*
|
||||
* @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId
|
||||
* @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as
|
||||
@@ -876,7 +1042,7 @@ public class UncachedKeyRing {
|
||||
}
|
||||
|
||||
// remember which certs we already added. this is cheaper than semantic deduplication
|
||||
Set<byte[]> certs = new TreeSet<byte[]>(new Comparator<byte[]>() {
|
||||
Set<byte[]> certs = new TreeSet<>(new Comparator<byte[]>() {
|
||||
public int compare(byte[] left, byte[] right) {
|
||||
// check for length equality
|
||||
if (left.length != right.length) {
|
||||
@@ -958,7 +1124,7 @@ public class UncachedKeyRing {
|
||||
if (signaturesIt == null) {
|
||||
continue;
|
||||
}
|
||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(signaturesIt)) {
|
||||
for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
|
||||
// Don't merge foreign stuff into secret keys
|
||||
if (cert.getKeyID() != masterKeyId && isSecret()) {
|
||||
continue;
|
||||
@@ -973,6 +1139,32 @@ public class UncachedKeyRing {
|
||||
modified = PGPPublicKey.addCertification(modified, rawUserId, cert);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over all user attribute certificates
|
||||
for (PGPUserAttributeSubpacketVector vector :
|
||||
new IterableIterator<PGPUserAttributeSubpacketVector>(key.getUserAttributes())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<PGPSignature> signaturesIt = key.getSignaturesForUserAttribute(vector);
|
||||
// no signatures for this user attribute attribute, skip it
|
||||
if (signaturesIt == null) {
|
||||
continue;
|
||||
}
|
||||
for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
|
||||
// Don't merge foreign stuff into secret keys
|
||||
if (cert.getKeyID() != masterKeyId && isSecret()) {
|
||||
continue;
|
||||
}
|
||||
byte[] encoded = cert.getEncoded();
|
||||
// Known cert, skip it
|
||||
if (certs.contains(encoded)) {
|
||||
continue;
|
||||
}
|
||||
newCerts += 1;
|
||||
certs.add(encoded);
|
||||
modified = PGPPublicKey.addCertification(modified, vector, cert);
|
||||
}
|
||||
}
|
||||
|
||||
// If anything changed, save the updated (sub)key
|
||||
if (modified != resultKey) {
|
||||
result = replacePublicKey(result, modified);
|
||||
|
||||
@@ -20,10 +20,10 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.ECPublicBCPGKey;
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
@@ -135,7 +135,7 @@ public class UncachedPublicKey {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PGPSignature sig : new IterableIterator<PGPSignature>(signaturesIt)) {
|
||||
for (PGPSignature sig : new IterableIterator<>(signaturesIt)) {
|
||||
try {
|
||||
|
||||
// if this is a revocation, this is not the user id
|
||||
@@ -199,7 +199,7 @@ public class UncachedPublicKey {
|
||||
}
|
||||
|
||||
public ArrayList<String> getUnorderedUserIds() {
|
||||
ArrayList<String> userIds = new ArrayList<String>();
|
||||
ArrayList<String> userIds = new ArrayList<>();
|
||||
for (byte[] rawUserId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
|
||||
// use our decoding method
|
||||
userIds.add(Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId));
|
||||
@@ -208,13 +208,22 @@ public class UncachedPublicKey {
|
||||
}
|
||||
|
||||
public ArrayList<byte[]> getUnorderedRawUserIds() {
|
||||
ArrayList<byte[]> userIds = new ArrayList<byte[]>();
|
||||
ArrayList<byte[]> userIds = new ArrayList<>();
|
||||
for (byte[] userId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
|
||||
userIds.add(userId);
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
public ArrayList<WrappedUserAttribute> getUnorderedUserAttributes() {
|
||||
ArrayList<WrappedUserAttribute> userAttributes = new ArrayList<>();
|
||||
for (PGPUserAttributeSubpacketVector userAttribute :
|
||||
new IterableIterator<PGPUserAttributeSubpacketVector>(mPublicKey.getUserAttributes())) {
|
||||
userAttributes.add(new WrappedUserAttribute(userAttribute));
|
||||
}
|
||||
return userAttributes;
|
||||
}
|
||||
|
||||
public boolean isElGamalEncrypt() {
|
||||
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
|
||||
}
|
||||
@@ -270,33 +279,83 @@ public class UncachedPublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator<WrappedSignature> getSignaturesForUserAttribute(WrappedUserAttribute attribute) {
|
||||
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForUserAttribute(attribute.getVector());
|
||||
if (it != null) {
|
||||
return new Iterator<WrappedSignature>() {
|
||||
public void remove() {
|
||||
it.remove();
|
||||
}
|
||||
public WrappedSignature next() {
|
||||
return new WrappedSignature(it.next());
|
||||
}
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all key usage flags.
|
||||
* If at least one key flag subpacket is present return these. If no
|
||||
* subpacket is present it returns null.
|
||||
*
|
||||
* Note that this method has package visiblity because it is used in test
|
||||
* cases. Certificates of UncachedPublicKey instances can NOT be assumed to
|
||||
* be verified, so the result of this method should not be used in other
|
||||
* places!
|
||||
* be verified or even by the correct key, so the result of this method
|
||||
* should never be used in other places!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
Integer getKeyUsage() {
|
||||
if (mCacheUsage == null) {
|
||||
PGPSignature mostRecentSig = null;
|
||||
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
|
||||
if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
|
||||
switch (sig.getSignatureType()) {
|
||||
case PGPSignature.DEFAULT_CERTIFICATION:
|
||||
case PGPSignature.POSITIVE_CERTIFICATION:
|
||||
case PGPSignature.CASUAL_CERTIFICATION:
|
||||
case PGPSignature.NO_CERTIFICATION:
|
||||
case PGPSignature.SUBKEY_BINDING:
|
||||
break;
|
||||
// if this is not one of the above types, don't care
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have no sig yet, take the first we can get
|
||||
if (mostRecentSig == null) {
|
||||
mostRecentSig = sig;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the new sig is less recent, skip it
|
||||
if (mostRecentSig.getCreationTime().after(sig.getCreationTime())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, note it down as the new "most recent" one
|
||||
mostRecentSig = sig;
|
||||
}
|
||||
|
||||
// Initialize to 0 as cached but empty value, if there is no sig (can't happen
|
||||
// for canonicalized keyring), or there is no KEY_FLAGS packet in the sig
|
||||
mCacheUsage = 0;
|
||||
if (mostRecentSig != null) {
|
||||
// If a mostRecentSig has been found, (cache and) return its flags
|
||||
PGPSignatureSubpacketVector hashed = mostRecentSig.getHashedSubPackets();
|
||||
if (hashed != null && hashed.getSubpacket(SignatureSubpacketTags.KEY_FLAGS) != null) {
|
||||
// init if at least one key flag subpacket has been found
|
||||
if (mCacheUsage == null) {
|
||||
mCacheUsage = 0;
|
||||
}
|
||||
mCacheUsage |= hashed.getKeyFlags();
|
||||
mCacheUsage = hashed.getKeyFlags();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return mCacheUsage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureList;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -79,7 +79,7 @@ public class WrappedSignature {
|
||||
}
|
||||
|
||||
public ArrayList<WrappedSignature> getEmbeddedSignatures() {
|
||||
ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>();
|
||||
ArrayList<WrappedSignature> sigs = new ArrayList<>();
|
||||
if (!mSig.hasSubpackets()) {
|
||||
return sigs;
|
||||
}
|
||||
@@ -199,12 +199,23 @@ public class WrappedSignature {
|
||||
}
|
||||
}
|
||||
|
||||
boolean verifySignature(PGPPublicKey key, PGPUserAttributeSubpacketVector attribute) throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verifyCertification(attribute, key);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifySignature(UncachedPublicKey key, byte[] rawUserId) throws PgpGeneralException {
|
||||
return verifySignature(key.getPublicKey(), rawUserId);
|
||||
}
|
||||
public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
|
||||
return verifySignature(key.getPublicKey(), uid);
|
||||
}
|
||||
public boolean verifySignature(UncachedPublicKey key, WrappedUserAttribute attribute) throws PgpGeneralException {
|
||||
return verifySignature(key.getPublicKey(), attribute.getVector());
|
||||
}
|
||||
|
||||
public static WrappedSignature fromBytes(byte[] data) {
|
||||
PGPObjectFactory factory = new PGPObjectFactory(data);
|
||||
@@ -243,7 +254,7 @@ public class WrappedSignature {
|
||||
}
|
||||
|
||||
public HashMap<String,String> getNotation() {
|
||||
HashMap<String,String> result = new HashMap<String,String>();
|
||||
HashMap<String,String> result = new HashMap<>();
|
||||
|
||||
// If there is any notation data
|
||||
if (mSig.getHashedSubPackets() != null
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.BCPGInputStream;
|
||||
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||
import org.spongycastle.bcpg.Packet;
|
||||
import org.spongycastle.bcpg.UserAttributePacket;
|
||||
import org.spongycastle.bcpg.UserAttributeSubpacket;
|
||||
import org.spongycastle.bcpg.UserAttributeSubpacketInputStream;
|
||||
import org.spongycastle.bcpg.UserAttributeSubpacketTags;
|
||||
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class WrappedUserAttribute implements Serializable {
|
||||
|
||||
public static final int UAT_NONE = 0;
|
||||
public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE;
|
||||
|
||||
private PGPUserAttributeSubpacketVector mVector;
|
||||
|
||||
WrappedUserAttribute(PGPUserAttributeSubpacketVector vector) {
|
||||
mVector = vector;
|
||||
}
|
||||
|
||||
PGPUserAttributeSubpacketVector getVector() {
|
||||
return mVector;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray();
|
||||
if (subpackets.length > 0) {
|
||||
return subpackets[0].getType();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static WrappedUserAttribute fromSubpacket (int type, byte[] data) {
|
||||
UserAttributeSubpacket subpacket = new UserAttributeSubpacket(type, data);
|
||||
PGPUserAttributeSubpacketVector vector = new PGPUserAttributeSubpacketVector(
|
||||
new UserAttributeSubpacket[] { subpacket });
|
||||
|
||||
return new WrappedUserAttribute(vector);
|
||||
|
||||
}
|
||||
|
||||
public byte[] getEncoded () throws IOException {
|
||||
UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
for (UserAttributeSubpacket subpacket : subpackets) {
|
||||
subpacket.encode(out);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static WrappedUserAttribute fromData (byte[] data) throws IOException {
|
||||
UserAttributeSubpacketInputStream in =
|
||||
new UserAttributeSubpacketInputStream(new ByteArrayInputStream(data));
|
||||
ArrayList<UserAttributeSubpacket> list = new ArrayList<UserAttributeSubpacket>();
|
||||
while (in.available() > 0) {
|
||||
list.add(in.readPacket());
|
||||
}
|
||||
UserAttributeSubpacket[] result = new UserAttributeSubpacket[list.size()];
|
||||
list.toArray(result);
|
||||
return new WrappedUserAttribute(
|
||||
new PGPUserAttributeSubpacketVector(result));
|
||||
}
|
||||
|
||||
/** Writes this object to an ObjectOutputStream. */
|
||||
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
BCPGOutputStream bcpg = new BCPGOutputStream(baos);
|
||||
bcpg.writePacket(new UserAttributePacket(mVector.toSubpacketArray()));
|
||||
out.writeObject(baos.toByteArray());
|
||||
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
|
||||
byte[] data = (byte[]) in.readObject();
|
||||
BCPGInputStream bcpg = new BCPGInputStream(new ByteArrayInputStream(data));
|
||||
Packet p = bcpg.readPacket();
|
||||
if ( ! UserAttributePacket.class.isInstance(p)) {
|
||||
throw new IOException("Could not decode UserAttributePacket!");
|
||||
}
|
||||
mVector = new PGPUserAttributeSubpacketVector(((UserAttributePacket) p).getSubpackets());
|
||||
|
||||
}
|
||||
|
||||
private void readObjectNoData() throws ObjectStreamException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!WrappedUserAttribute.class.isInstance(o)) {
|
||||
return false;
|
||||
}
|
||||
return mVector.equals(((WrappedUserAttribute) o).mVector);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user