Reuse signature creation timestamp for synchronous signing

This commit is contained in:
Dominik Schürmann 2014-07-22 18:09:12 +02:00
parent afd6851e5b
commit 99af2c33d3
4 changed files with 45 additions and 34 deletions

View File

@ -18,10 +18,8 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.openkeychain.nfc.NfcHandler;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream; import org.spongycastle.bcpg.BCPGOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPCompressedDataGenerator; import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator; import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
@ -41,17 +39,14 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
@ -77,7 +72,9 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase; private String mSignaturePassphrase;
private boolean mEncryptToSigner; private boolean mEncryptToSigner;
private boolean mCleartextInput; private boolean mCleartextInput;
private byte[] mNfcData;
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
private static byte[] NEW_LINE; private static byte[] NEW_LINE;
@ -108,7 +105,8 @@ public class PgpSignEncrypt {
this.mSignaturePassphrase = builder.mSignaturePassphrase; this.mSignaturePassphrase = builder.mSignaturePassphrase;
this.mEncryptToSigner = builder.mEncryptToSigner; this.mEncryptToSigner = builder.mEncryptToSigner;
this.mCleartextInput = builder.mCleartextInput; this.mCleartextInput = builder.mCleartextInput;
this.mNfcData = builder.mNfcData; this.mNfcSignedHash = builder.mNfcSignedHash;
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
} }
public static class Builder { public static class Builder {
@ -131,7 +129,9 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase = null; private String mSignaturePassphrase = null;
private boolean mEncryptToSigner = false; private boolean mEncryptToSigner = false;
private boolean mCleartextInput = false; private boolean mCleartextInput = false;
private byte[] mNfcData = null;
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) { public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper; this.mProviderHelper = providerHelper;
@ -216,8 +216,9 @@ public class PgpSignEncrypt {
return this; return this;
} }
public Builder setNfcData(byte[] nfcData) { public Builder setNfcState(byte[] signedHash, Date creationTimestamp) {
mNfcData = nfcData; mNfcSignedHash = signedHash;
mNfcCreationTimestamp = creationTimestamp;
return this; return this;
} }
@ -259,19 +260,15 @@ public class PgpSignEncrypt {
} }
public static class NeedNfcDataException extends Exception { public static class NeedNfcDataException extends Exception {
public byte[] mData; public byte[] mHashToSign;
public Date mCreationTimestamp;
public NeedNfcDataException(byte[] data) { public NeedNfcDataException(byte[] hashToSign, Date creationTimestamp) {
mData = data; mHashToSign = hashToSign;
mCreationTimestamp = creationTimestamp;
} }
} }
// TODO: remove later
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
/** /**
* Signs and/or encrypts data based on parameters of class * Signs and/or encrypts data based on parameters of class
*/ */
@ -381,7 +378,7 @@ public class PgpSignEncrypt {
mSignatureHashAlgorithm, cleartext); mSignatureHashAlgorithm, cleartext);
} else { } else {
signatureGenerator = signingKey.getSignatureGenerator( signatureGenerator = signingKey.getSignatureGenerator(
mSignatureHashAlgorithm, cleartext, mNfcData); mSignatureHashAlgorithm, cleartext, mNfcSignedHash, mNfcCreationTimestamp);
} }
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
// TODO throw correct type of exception (which shouldn't be PGPException) // TODO throw correct type of exception (which shouldn't be PGPException)
@ -546,8 +543,8 @@ public class PgpSignEncrypt {
try { try {
signatureGenerator.generate().encode(pOut); signatureGenerator.generate().encode(pOut);
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) { } catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
// this secret key diverts to a OpenPGP card, throw exception with to-be-signed hash // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
throw new NeedNfcDataException(e.hashToSign); throw new NeedNfcDataException(e.hashToSign, e.creationTimestamp);
} }
} }
} }

View File

@ -1,7 +1,6 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.S2K; import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
@ -30,11 +29,9 @@ import org.sufficientlysecure.keychain.util.Log;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.ArrayList; import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
/** Wrapper for a PGPSecretKey. /** Wrapper for a PGPSecretKey.
* *
@ -121,7 +118,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
} }
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
byte[] nfcSignedHash) byte[] nfcSignedHash, Date nfcCreationTimestamp)
throws PgpGeneralException { throws PgpGeneralException {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException(); throw new PrivateKeyNotUnlockedException();
@ -129,11 +126,21 @@ public class WrappedSecretKey extends WrappedPublicKey {
PGPContentSignerBuilder contentSignerBuilder; PGPContentSignerBuilder contentSignerBuilder;
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
// to sign using nfc PgpSignEncrypt is executed two times.
// the first time it stops to return the PendingIntent for nfc connection and signing the hash
// the second time the signed hash is used.
// to get the same hash we cache the timestamp for the second round!
if (nfcCreationTimestamp == null) {
nfcCreationTimestamp = new Date();
}
// use synchronous "NFC based" SignerBuilder // use synchronous "NFC based" SignerBuilder
contentSignerBuilder = new NfcSyncPGPContentSignerBuilder( contentSignerBuilder = new NfcSyncPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo, mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
mSecretKey.getKeyID(), nfcSignedHash) mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Log.d(Constants.TAG, "mSecretKey.getKeyID() "+ PgpKeyHelper.convertKeyIdToHex(mSecretKey.getKeyID()));
} else { } else {
// content signer based on signing key algorithm and chosen hash algorithm // content signer based on signing key algorithm and chosen hash algorithm
contentSignerBuilder = new JcaPGPContentSignerBuilder( contentSignerBuilder = new JcaPGPContentSignerBuilder(
@ -155,6 +162,10 @@ public class WrappedSecretKey extends WrappedPublicKey {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback()); spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
if (nfcCreationTimestamp != null) {
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
}
signatureGenerator.setHashedSubpackets(spGen.generate()); signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator; return signatureGenerator;
} catch(PGPException e) { } catch(PGPException e) {

View File

@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.Set; import java.util.Set;
public class OpenPgpService extends RemoteService { public class OpenPgpService extends RemoteService {
@ -136,11 +137,11 @@ public class OpenPgpService extends RemoteService {
return result; return result;
} }
private Intent getNfcIntent(Intent data, byte[] in) { private Intent getNfcIntent(Intent data, byte[] hashToSign) {
// build PendingIntent for Yubikey NFC operations // build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class); Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN_HASH); intent.setAction(NfcActivity.ACTION_SIGN_HASH);
intent.putExtra(NfcActivity.EXTRA_NFC_DATA, in); intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// pass params through to activity that it can be returned again later to repeat pgp operation // pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(NfcActivity.EXTRA_DATA, data); intent.putExtra(NfcActivity.EXTRA_DATA, data);
@ -191,7 +192,8 @@ public class OpenPgpService extends RemoteService {
return passphraseBundle; return passphraseBundle;
} }
byte[] nfcData = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DATA); byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
Date nfcCreationTimestamp = new Date(data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, 0));
// Get Input- and OutputStream from ParcelFileDescriptor // Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
@ -210,7 +212,7 @@ public class OpenPgpService extends RemoteService {
.setSignatureForceV3(false) .setSignatureForceV3(false)
.setSignatureMasterKeyId(accSettings.getKeyId()) .setSignatureMasterKeyId(accSettings.getKeyId())
.setSignaturePassphrase(passphrase) .setSignaturePassphrase(passphrase)
.setNfcData(nfcData); .setNfcState(nfcSignedHash, nfcCreationTimestamp);
// TODO: currently always assume cleartext input, no sign-only of binary currently! // TODO: currently always assume cleartext input, no sign-only of binary currently!
builder.setCleartextInput(true); builder.setCleartextInput(true);
@ -229,7 +231,8 @@ public class OpenPgpService extends RemoteService {
throw new Exception(getString(R.string.error_no_signature_key)); throw new Exception(getString(R.string.error_no_signature_key));
} catch (PgpSignEncrypt.NeedNfcDataException e) { } catch (PgpSignEncrypt.NeedNfcDataException e) {
// return PendingIntent to execute NFC activity // return PendingIntent to execute NFC activity
Intent nfcIntent = getNfcIntent(data, e.mData); data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, e.mCreationTimestamp.getTime());
Intent nfcIntent = getNfcIntent(data, e.mHashToSign);
return nfcIntent; return nfcIntent;
} }
} finally { } finally {

@ -1 +1 @@
Subproject commit bedea7f2734451e7990d4971cb7f9f4e3cbe2fee Subproject commit 6402aa9232d6de01c696dc6ba23ca86355474d4e