mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
Reuse signature creation timestamp for synchronous signing
This commit is contained in:
parent
afd6851e5b
commit
99af2c33d3
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
2
extern/openpgp-card-nfc-lib
vendored
2
extern/openpgp-card-nfc-lib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit bedea7f2734451e7990d4971cb7f9f4e3cbe2fee
|
Subproject commit 6402aa9232d6de01c696dc6ba23ca86355474d4e
|
Loading…
Reference in New Issue
Block a user