introduce handling of notation direct key signatures, and experimental pin notation packet

This commit is contained in:
Vincent Breitmoser
2014-12-30 00:44:12 +01:00
parent 576e6fd0cc
commit c7b0b650c1
13 changed files with 126 additions and 24 deletions

View File

@@ -370,13 +370,15 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_ERROR_MASTER_ALGO (LogLevel.ERROR, R.string.msg_kc_error_master_algo),
MSG_KC_ERROR_DUP_KEY (LogLevel.ERROR, R.string.msg_kc_error_dup_key),
MSG_KC_MASTER (LogLevel.DEBUG, R.string.msg_kc_master),
MSG_KC_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_bad_type),
MSG_KC_REVOKE_BAD_ERR (LogLevel.WARN, R.string.msg_kc_revoke_bad_err),
MSG_KC_REVOKE_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_revoke_bad_local),
MSG_KC_REVOKE_BAD_TIME (LogLevel.WARN, R.string.msg_kc_revoke_bad_time),
MSG_KC_REVOKE_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_revoke_bad_type),
MSG_KC_REVOKE_BAD_TYPE_UID (LogLevel.WARN, R.string.msg_kc_revoke_bad_type_uid),
MSG_KC_REVOKE_BAD (LogLevel.WARN, R.string.msg_kc_revoke_bad),
MSG_KC_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_revoke_dup),
MSG_KC_NOTATION_DUP (LogLevel.DEBUG, R.string.msg_kc_notation_dup),
MSG_KC_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_kc_notation_empty),
MSG_KC_SUB (LogLevel.DEBUG, R.string.msg_kc_sub),
MSG_KC_SUB_BAD(LogLevel.WARN, R.string.msg_kc_sub_bad),
MSG_KC_SUB_BAD_ERR(LogLevel.WARN, R.string.msg_kc_sub_bad_err),

View File

@@ -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.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
@@ -877,7 +878,8 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
sKR = applyNewUnlock(sKR, masterPublicKey, passphrase, saveParcel.mNewUnlock, log, indent);
sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
passphrase, saveParcel.mNewUnlock, log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
@@ -909,19 +911,60 @@ public class PgpKeyOperation {
private static PGPSecretKeyRing applyNewUnlock(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
PGPPrivateKey masterPrivateKey,
String passphrase,
ChangeUnlockParcel newUnlock,
OperationLog log, int indent) throws PGPException {
if (newUnlock.mNewPassphrase != null) {
return applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
// add packet with EMPTY notation data (updates old one, but will be stripped later)
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
hashedPacketsGen.setExportable(false, false);
sGen.setHashedSubpackets(hashedPacketsGen.generate());
}
sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey);
return sKR;
}
if (newUnlock.mNewPin != null) {
sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
// add packet with EMPTY notation data (updates old one, but will be stripped later)
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
hashedPacketsGen.setExportable(false, false);
hashedPacketsGen.setNotationData(false, false, "pin@unlock.sufficientlysecure.org", "1");
sGen.setHashedSubpackets(hashedPacketsGen.generate());
}
sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey);
return sKR;
}
throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
}
private static PGPSecretKeyRing applyNewPassphrase(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,

View File

@@ -41,7 +41,6 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Utf8Util;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -302,6 +301,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
PGPSignature notation = null;
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
@@ -318,9 +318,9 @@ public class UncachedKeyRing {
}
WrappedSignature cert = new WrappedSignature(zert);
if (type != PGPSignature.KEY_REVOCATION) {
if (type != PGPSignature.KEY_REVOCATION && type != PGPSignature.DIRECT_KEY) {
// Unknown type, just remove
log.add(LogType.MSG_KC_REVOKE_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
log.add(LogType.MSG_KC_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -334,14 +334,6 @@ public class UncachedKeyRing {
continue;
}
if (cert.isLocal()) {
// Remove revocation certs with "local" flag
log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
try {
cert.init(masterKey);
if (!cert.verifySignature(masterKey)) {
@@ -357,6 +349,41 @@ public class UncachedKeyRing {
continue;
}
if (cert.isLocal()) {
// Remove revocation certs with "local" flag
log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
// special case: direct key signatures!
if (cert.getSignatureType() == PGPSignature.DIRECT_KEY) {
// must be local, otherwise strip!
if (!cert.isLocal()) {
log.add(LogType.MSG_KC_BAD_TYPE, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
// first notation? fine then.
if (notation == null) {
notation = zert;
// more notations? at least one is superfluous, then.
} else if (notation.getCreationTime().before(zert.getCreationTime())) {
log.add(LogType.MSG_KC_NOTATION_DUP, indent);
modified = PGPPublicKey.removeCertification(modified, notation);
redundantCerts += 1;
notation = zert;
} else {
log.add(LogType.MSG_KC_NOTATION_DUP, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
redundantCerts += 1;
}
continue;
}
// first revocation? fine then.
if (revocation == null) {
revocation = zert;
@@ -373,6 +400,16 @@ public class UncachedKeyRing {
}
}
// If we have a notation packet, check if there is even any data in it?
if (notation != null) {
// If there isn't, might as well strip it
if (new WrappedSignature(notation).getNotation().isEmpty()) {
log.add(LogType.MSG_KC_NOTATION_EMPTY, indent);
modified = PGPPublicKey.removeCertification(modified, notation);
redundantCerts += 1;
}
}
ArrayList<String> processedUserIds = new ArrayList<String>();
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);

View File

@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.Exportable;
import org.spongycastle.bcpg.sig.NotationData;
import org.spongycastle.bcpg.sig.Revocable;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
@@ -37,6 +38,7 @@ import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
/** OpenKeychain wrapper around PGPSignature objects.
*
@@ -239,4 +241,20 @@ public class WrappedSignature {
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE);
return ! ((Exportable) p).isExportable();
}
public HashMap<String,byte[]> getNotation() {
HashMap<String,byte[]> result = new HashMap<String,byte[]>();
// If there is any notation data
if (mSig.getHashedSubPackets() != null
&& mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.NOTATION_DATA)) {
// Iterate over notation data
for (NotationData data : mSig.getHashedSubPackets().getNotationDataOccurrences()) {
result.put(data.getNotationName(), data.getNotationValueBytes());
}
}
return result;
}
}