aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-03-18 18:25:44 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-03-18 18:25:44 +0100
commitaca54e31eae450e7deec54cca6654ee202c7a90f (patch)
tree19832f3076bc13b65eca38b2a4862e0bf39c4776 /OpenKeychain/src/main/java/org
parent4499caef1e64d2e1afec37d360958f516da4dd40 (diff)
downloadopen-keychain-aca54e31eae450e7deec54cca6654ee202c7a90f.tar.gz
open-keychain-aca54e31eae450e7deec54cca6654ee202c7a90f.tar.bz2
open-keychain-aca54e31eae450e7deec54cca6654ee202c7a90f.zip
generalize nfc crypto input structure
Diffstat (limited to 'OpenKeychain/src/main/java/org')
-rw-r--r--OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java15
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java47
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java)139
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java81
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java49
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java492
16 files changed, 872 insertions, 230 deletions
diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java
index e0286ec15..0344b2173 100644
--- a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java
+++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java
@@ -14,8 +14,12 @@ import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.security.Provider;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* This class is based on JcaPGPContentSignerBuilder.
@@ -31,31 +35,27 @@ public class NfcSyncPGPContentSignerBuilder
private int keyAlgorithm;
private long keyID;
- private byte[] signedHash;
- private Date creationTimestamp;
+ private Map signedHashes;
public static class NfcInteractionNeeded extends RuntimeException
{
public byte[] hashToSign;
- public Date creationTimestamp;
public int hashAlgo;
- public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo, Date creationTimestamp)
+ public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo)
{
super("NFC interaction required!");
this.hashToSign = hashToSign;
this.hashAlgo = hashAlgo;
- this.creationTimestamp = creationTimestamp;
}
}
- public NfcSyncPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm, long keyID, byte[] signedHash, Date creationTimestamp)
+ public NfcSyncPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm, long keyID, Map signedHashes)
{
this.keyAlgorithm = keyAlgorithm;
this.hashAlgorithm = hashAlgorithm;
this.keyID = keyID;
- this.signedHash = signedHash;
- this.creationTimestamp = creationTimestamp;
+ this.signedHashes = signedHashes;
}
public NfcSyncPGPContentSignerBuilder setProvider(Provider provider)
@@ -125,14 +125,14 @@ public class NfcSyncPGPContentSignerBuilder
}
public byte[] getSignature() {
- if (signedHash != null) {
- // we already have the signed hash from a previous execution, return this!
- return signedHash;
- } else {
- // catch this when signatureGenerator.generate() is executed and divert digest to card,
- // when doing the operation again reuse creationTimestamp (this will be hashed)
- throw new NfcInteractionNeeded(digestCalculator.getDigest(), getHashAlgorithm(), creationTimestamp);
+ byte[] digest = digestCalculator.getDigest();
+ ByteBuffer buf = ByteBuffer.wrap(digest);
+ if (signedHashes.containsKey(buf)) {
+ return (byte[]) signedHashes.get(buf);
}
+ // catch this when signatureGenerator.generate() is executed and divert digest to card,
+ // when doing the operation again reuse creationTimestamp (this will be hashed)
+ throw new NfcInteractionNeeded(digest, getHashAlgorithm());
}
public byte[] getDigest()
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
index de2f64404..a1204c0b5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
@@ -35,7 +35,6 @@ public class PgpSignEncryptResult extends OperationResult {
long mNfcKeyId;
byte[] mNfcHash;
int mNfcAlgo;
- Date mNfcTimestamp;
String mNfcPassphrase;
byte[] mDetachedSignature;
@@ -47,11 +46,10 @@ public class PgpSignEncryptResult extends OperationResult {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
}
- public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) {
+ public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, String passphrase) {
mNfcKeyId = nfcKeyId;
mNfcHash = nfcHash;
mNfcAlgo = nfcAlgo;
- mNfcTimestamp = nfcTimestamp;
mNfcPassphrase = passphrase;
}
@@ -71,10 +69,6 @@ public class PgpSignEncryptResult extends OperationResult {
return mNfcAlgo;
}
- public Date getNfcTimestamp() {
- return mNfcTimestamp;
- }
-
public String getNfcPassphrase() {
return mNfcPassphrase;
}
@@ -95,7 +89,6 @@ public class PgpSignEncryptResult extends OperationResult {
super(source);
mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;
mNfcAlgo = source.readInt();
- mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null;
mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null;
}
@@ -112,12 +105,6 @@ public class PgpSignEncryptResult extends OperationResult {
dest.writeInt(0);
}
dest.writeInt(mNfcAlgo);
- if (mNfcTimestamp != null) {
- dest.writeInt(1);
- dest.writeLong(mNfcTimestamp.getTime());
- } else {
- dest.writeInt(0);
- }
if (mDetachedSignature != null) {
dest.writeInt(1);
dest.writeByteArray(mDetachedSignature);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
index ed0de65b0..23e8094b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
@@ -21,6 +21,9 @@ import android.os.Parcel;
import java.util.ArrayList;
+import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
+
+
public class SignEncryptResult extends OperationResult {
ArrayList<PgpSignEncryptResult> mResults;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index ab91d7747..df409902f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -42,10 +42,13 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.Log;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+
/**
* Wrapper for a PGPSecretKey.
@@ -183,13 +186,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return PgpConstants.sPreferredHashAlgorithms;
}
- private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash,
- Date nfcCreationTimestamp) {
+ private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
+ Map<ByteBuffer,byte[]> signedHashes) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
// use synchronous "NFC based" SignerBuilder
return new NfcSyncPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
- mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp)
+ mSecretKey.getKeyID(), signedHashes)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
} else {
// content signer based on signing key algorithm and chosen hash algorithm
@@ -200,28 +203,24 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
- byte[] nfcSignedHash, Date nfcCreationTimestamp)
+ Map<ByteBuffer,byte[]> signedHashes, Date creationTimestamp)
throws PgpGeneralException {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
- if (nfcSignedHash != null && nfcCreationTimestamp == null) {
- throw new PgpGeneralException("Got nfc hash without timestamp!!");
- }
// We explicitly create a signature creation timestamp in this place.
// That way, we can inject an artificial one from outside, ie the one
// used in previous runs of this function.
- if (nfcCreationTimestamp == null) {
+ if (creationTimestamp == null) {
// 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!
- nfcCreationTimestamp = new Date();
+ creationTimestamp = new Date();
}
- PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo,
- nfcSignedHash, nfcCreationTimestamp);
+ PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, signedHashes);
int signatureType;
if (cleartext) {
@@ -237,7 +236,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
- spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
+ spGen.setSignatureCreationTime(false, creationTimestamp);
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch (PgpKeyNotFoundException | PGPException e) {
@@ -267,8 +266,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
* @param userIds User IDs to certify
* @return A keyring with added certifications
*/
- public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
- byte[] nfcSignedHash, Date nfcCreationTimestamp) {
+ public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing,
+ List<String> userIds,
+ HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
@@ -283,7 +283,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
PGPSignatureGenerator signatureGenerator;
{
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
- PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp);
+ PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
try {
@@ -296,9 +296,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- if (nfcCreationTimestamp != null) {
- spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
- Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
+ if (creationTimestamp != null) {
+ spGen.setSignatureCreationTime(false, creationTimestamp);
+ Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
}
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
@@ -331,7 +331,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
* @return A keyring with added certifications
*/
public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing,
- List<WrappedUserAttribute> userAttributes, byte[] nfcSignedHash, Date nfcCreationTimestamp) {
+ List<WrappedUserAttribute> userAttributes,
+ HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
@@ -346,7 +347,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
PGPSignatureGenerator signatureGenerator;
{
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
- PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp);
+ PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
try {
@@ -359,9 +360,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- if (nfcCreationTimestamp != null) {
- spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
- Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
+ if (creationTimestamp != null) {
+ spGen.setSignatureCreationTime(false, creationTimestamp);
+ Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
}
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
index 2dec4b9c2..00ecc179e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
@@ -20,10 +20,17 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import java.nio.ByteBuffer;
import java.util.Date;
+import java.util.Map;
-public class PgpSignEncryptInput {
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class PgpSignEncryptInputParcel implements Parcelable {
protected String mVersionHeader = null;
protected boolean mEnableAsciiArmorOutput = false;
@@ -36,13 +43,71 @@ public class PgpSignEncryptInput {
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
protected String mSignaturePassphrase = null;
protected long mAdditionalEncryptId = Constants.key.none;
- protected byte[] mNfcSignedHash = null;
- protected Date mNfcCreationTimestamp = null;
protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset;
protected boolean mCleartextSignature;
protected boolean mDetachedSignature = false;
protected boolean mHiddenRecipients = false;
+ protected CryptoInputParcel mCryptoInput = new CryptoInputParcel(null);
+
+ public PgpSignEncryptInputParcel() {
+
+ }
+
+ PgpSignEncryptInputParcel(Parcel source) {
+
+ // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
+ mVersionHeader = source.readString();
+ mEnableAsciiArmorOutput = source.readInt() == 1;
+ mCompressionId = source.readInt();
+ mEncryptionMasterKeyIds = source.createLongArray();
+ mSymmetricPassphrase = source.readString();
+ mSymmetricEncryptionAlgorithm = source.readInt();
+ mSignatureMasterKeyId = source.readLong();
+ mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null;
+ mSignatureHashAlgorithm = source.readInt();
+ mSignaturePassphrase = source.readString();
+ mAdditionalEncryptId = source.readLong();
+ mFailOnMissingEncryptionKeyIds = source.readInt() == 1;
+ mCharset = source.readString();
+ mCleartextSignature = source.readInt() == 1;
+ mDetachedSignature = source.readInt() == 1;
+ mHiddenRecipients = source.readInt() == 1;
+
+ mCryptoInput = source.readParcelable(PgpSignEncryptInputParcel.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mVersionHeader);
+ dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
+ dest.writeInt(mCompressionId);
+ dest.writeLongArray(mEncryptionMasterKeyIds);
+ dest.writeString(mSymmetricPassphrase);
+ dest.writeInt(mSymmetricEncryptionAlgorithm);
+ dest.writeLong(mSignatureMasterKeyId);
+ if (mSignatureSubKeyId != null) {
+ dest.writeInt(1);
+ dest.writeLong(mSignatureSubKeyId);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mSignatureHashAlgorithm);
+ dest.writeString(mSignaturePassphrase);
+ dest.writeLong(mAdditionalEncryptId);
+ dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0);
+ dest.writeString(mCharset);
+ dest.writeInt(mCleartextSignature ? 1 : 0);
+ dest.writeInt(mDetachedSignature ? 1 : 0);
+ dest.writeInt(mHiddenRecipients ? 1 : 0);
+
+ dest.writeParcelable(mCryptoInput, 0);
+ }
public String getCharset() {
return mCharset;
@@ -56,19 +121,11 @@ public class PgpSignEncryptInput {
return mFailOnMissingEncryptionKeyIds;
}
- public Date getNfcCreationTimestamp() {
- return mNfcCreationTimestamp;
- }
-
- public byte[] getNfcSignedHash() {
- return mNfcSignedHash;
- }
-
public long getAdditionalEncryptId() {
return mAdditionalEncryptId;
}
- public PgpSignEncryptInput setAdditionalEncryptId(long additionalEncryptId) {
+ public PgpSignEncryptInputParcel setAdditionalEncryptId(long additionalEncryptId) {
mAdditionalEncryptId = additionalEncryptId;
return this;
}
@@ -77,7 +134,7 @@ public class PgpSignEncryptInput {
return mSignaturePassphrase;
}
- public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) {
+ public PgpSignEncryptInputParcel setSignaturePassphrase(String signaturePassphrase) {
mSignaturePassphrase = signaturePassphrase;
return this;
}
@@ -86,7 +143,7 @@ public class PgpSignEncryptInput {
return mSignatureHashAlgorithm;
}
- public PgpSignEncryptInput setSignatureHashAlgorithm(int signatureHashAlgorithm) {
+ public PgpSignEncryptInputParcel setSignatureHashAlgorithm(int signatureHashAlgorithm) {
mSignatureHashAlgorithm = signatureHashAlgorithm;
return this;
}
@@ -95,7 +152,7 @@ public class PgpSignEncryptInput {
return mSignatureSubKeyId;
}
- public PgpSignEncryptInput setSignatureSubKeyId(long signatureSubKeyId) {
+ public PgpSignEncryptInputParcel setSignatureSubKeyId(long signatureSubKeyId) {
mSignatureSubKeyId = signatureSubKeyId;
return this;
}
@@ -104,7 +161,7 @@ public class PgpSignEncryptInput {
return mSignatureMasterKeyId;
}
- public PgpSignEncryptInput setSignatureMasterKeyId(long signatureMasterKeyId) {
+ public PgpSignEncryptInputParcel setSignatureMasterKeyId(long signatureMasterKeyId) {
mSignatureMasterKeyId = signatureMasterKeyId;
return this;
}
@@ -113,7 +170,7 @@ public class PgpSignEncryptInput {
return mSymmetricEncryptionAlgorithm;
}
- public PgpSignEncryptInput setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
+ public PgpSignEncryptInputParcel setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
return this;
}
@@ -122,7 +179,7 @@ public class PgpSignEncryptInput {
return mSymmetricPassphrase;
}
- public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) {
+ public PgpSignEncryptInputParcel setSymmetricPassphrase(String symmetricPassphrase) {
mSymmetricPassphrase = symmetricPassphrase;
return this;
}
@@ -131,7 +188,7 @@ public class PgpSignEncryptInput {
return mEncryptionMasterKeyIds;
}
- public PgpSignEncryptInput setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
+ public PgpSignEncryptInputParcel setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
mEncryptionMasterKeyIds = encryptionMasterKeyIds;
return this;
}
@@ -140,7 +197,7 @@ public class PgpSignEncryptInput {
return mCompressionId;
}
- public PgpSignEncryptInput setCompressionId(int compressionId) {
+ public PgpSignEncryptInputParcel setCompressionId(int compressionId) {
mCompressionId = compressionId;
return this;
}
@@ -153,28 +210,22 @@ public class PgpSignEncryptInput {
return mVersionHeader;
}
- public PgpSignEncryptInput setVersionHeader(String versionHeader) {
+ public PgpSignEncryptInputParcel setVersionHeader(String versionHeader) {
mVersionHeader = versionHeader;
return this;
}
- public PgpSignEncryptInput setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
+ public PgpSignEncryptInputParcel setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
return this;
}
- public PgpSignEncryptInput setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
+ public PgpSignEncryptInputParcel setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
return this;
}
- public PgpSignEncryptInput setNfcState(byte[] signedHash, Date creationTimestamp) {
- mNfcSignedHash = signedHash;
- mNfcCreationTimestamp = creationTimestamp;
- return this;
- }
-
- public PgpSignEncryptInput setCleartextSignature(boolean cleartextSignature) {
+ public PgpSignEncryptInputParcel setCleartextSignature(boolean cleartextSignature) {
this.mCleartextSignature = cleartextSignature;
return this;
}
@@ -183,7 +234,7 @@ public class PgpSignEncryptInput {
return mCleartextSignature;
}
- public PgpSignEncryptInput setDetachedSignature(boolean detachedSignature) {
+ public PgpSignEncryptInputParcel setDetachedSignature(boolean detachedSignature) {
this.mDetachedSignature = detachedSignature;
return this;
}
@@ -192,7 +243,7 @@ public class PgpSignEncryptInput {
return mDetachedSignature;
}
- public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) {
+ public PgpSignEncryptInputParcel setHiddenRecipients(boolean hiddenRecipients) {
this.mHiddenRecipients = hiddenRecipients;
return this;
}
@@ -200,5 +251,29 @@ public class PgpSignEncryptInput {
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
+
+ public PgpSignEncryptInputParcel setCryptoInput(CryptoInputParcel cryptoInput) {
+ mCryptoInput = cryptoInput;
+ return this;
+ }
+
+ public Map<ByteBuffer, byte[]> getCryptoData() {
+ return mCryptoInput.getCryptoData();
+ }
+
+ public Date getSignatureTime() {
+ return mCryptoInput.getSignatureTime();
+ }
+
+ public static final Creator<PgpSignEncryptInputParcel> CREATOR = new Creator<PgpSignEncryptInputParcel>() {
+ public PgpSignEncryptInputParcel createFromParcel(final Parcel source) {
+ return new PgpSignEncryptInputParcel(source);
+ }
+
+ public PgpSignEncryptInputParcel[] newArray(final int size) {
+ return new PgpSignEncryptInputParcel[size];
+ }
+ };
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 94e04060d..2e515137a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -44,6 +44,8 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -71,7 +73,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
* For a high-level operation based on URIs, see SignEncryptOperation.
*
- * @see org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput
+ * @see PgpSignEncryptInputParcel
* @see org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult
* @see org.sufficientlysecure.keychain.operations.SignEncryptOperation
*
@@ -99,7 +101,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
/**
* Signs and/or encrypts data based on parameters of class
*/
- public PgpSignEncryptResult execute(PgpSignEncryptInput input,
+ public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input,
InputData inputData, OutputStream outputStream) {
int indent = 0;
@@ -282,7 +284,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
try {
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getSignatureGenerator(
- input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
+ input.getSignatureHashAlgorithm(), cleartext,
+ input.getCryptoData(), input.getSignatureTime());
} catch (PgpGeneralException e) {
log.add(LogType.MSG_PSE_ERROR_NFC, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
@@ -489,7 +492,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);
// Note that the checked key here is the master key, not the signing key
// (although these are always the same on Yubikeys)
- result.setNfcData(input.getSignatureSubKeyId(), e.hashToSign, e.hashAlgo, e.creationTimestamp, input.getSignaturePassphrase());
+ result.setNfcData(signingKey.getKeyId(), e.hashToSign, e.hashAlgo,
+ input.getSignaturePassphrase());
Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign));
return result;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
index 8e71e8815..1b14e78fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
@@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import android.os.Parcel;
-import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collection;
@@ -40,7 +39,7 @@ import java.util.List;
* left, which will be returned in a byte array as part of the result parcel.
*
*/
-public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable {
+public class SignEncryptParcel extends PgpSignEncryptInputParcel {
public ArrayList<Uri> mInputUris = new ArrayList<>();
public ArrayList<Uri> mOutputUris = new ArrayList<>();
@@ -51,26 +50,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
}
public SignEncryptParcel(Parcel src) {
-
- // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
- mVersionHeader = src.readString();
- mEnableAsciiArmorOutput = src.readInt() == 1;
- mCompressionId = src.readInt();
- mEncryptionMasterKeyIds = src.createLongArray();
- mSymmetricPassphrase = src.readString();
- mSymmetricEncryptionAlgorithm = src.readInt();
- mSignatureMasterKeyId = src.readLong();
- mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null;
- mSignatureHashAlgorithm = src.readInt();
- mSignaturePassphrase = src.readString();
- mAdditionalEncryptId = src.readLong();
- mNfcSignedHash = src.createByteArray();
- mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null;
- mFailOnMissingEncryptionKeyIds = src.readInt() == 1;
- mCharset = src.readString();
- mCleartextSignature = src.readInt() == 1;
- mDetachedSignature = src.readInt() == 1;
- mHiddenRecipients = src.readInt() == 1;
+ super(src);
mInputUris = src.createTypedArrayList(Uri.CREATOR);
mOutputUris = src.createTypedArrayList(Uri.CREATOR);
@@ -108,34 +88,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mVersionHeader);
- dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
- dest.writeInt(mCompressionId);
- dest.writeLongArray(mEncryptionMasterKeyIds);
- dest.writeString(mSymmetricPassphrase);
- dest.writeInt(mSymmetricEncryptionAlgorithm);
- dest.writeLong(mSignatureMasterKeyId);
- if (mSignatureSubKeyId != null) {
- dest.writeInt(1);
- dest.writeLong(mSignatureSubKeyId);
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(mSignatureHashAlgorithm);
- dest.writeString(mSignaturePassphrase);
- dest.writeLong(mAdditionalEncryptId);
- dest.writeByteArray(mNfcSignedHash);
- if (mNfcCreationTimestamp != null) {
- dest.writeInt(1);
- dest.writeLong(mNfcCreationTimestamp.getTime());
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0);
- dest.writeString(mCharset);
- dest.writeInt(mCleartextSignature ? 1 : 0);
- dest.writeInt(mDetachedSignature ? 1 : 0);
- dest.writeInt(mHiddenRecipients ? 1 : 0);
+ super.writeToParcel(dest, flags);
dest.writeTypedList(mInputUris);
dest.writeTypedList(mOutputUris);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index a4bc95602..f15bf7925 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEnt
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -48,6 +48,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
import org.sufficientlysecure.keychain.ui.NfcActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
@@ -258,11 +259,13 @@ public class OpenPgpService extends RemoteService {
}
// carefully: only set if timestamp exists
- Date nfcCreationDate = null;
+ Date nfcCreationDate;
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);
if (nfcCreationTimestamp != -1) {
nfcCreationDate = new Date(nfcCreationTimestamp);
+ } else {
+ nfcCreationDate = new Date();
}
// Get Input- and OutputStream from ParcelFileDescriptor
@@ -275,15 +278,18 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
+ CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate);
+ cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix
+
// sign-only
- PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
+ PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel()
.setEnableAsciiArmorOutput(asciiArmor)
.setCleartextSignature(cleartextSign)
.setDetachedSignature(!cleartextSign)
.setVersionHeader(null)
.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
.setSignatureMasterKeyId(signKeyId)
- .setNfcState(nfcSignedHash, nfcCreationDate);
+ .setCryptoInput(cryptoInput);
// execute PGP operation!
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
@@ -298,7 +304,7 @@ public class OpenPgpService extends RemoteService {
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
+ data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, nfcCreationDate.getTime());
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -389,7 +395,7 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength, originalFilename);
- PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
+ PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();
pseInput.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(null)
.setCompressionId(compressionId)
@@ -412,16 +418,21 @@ public class OpenPgpService extends RemoteService {
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
// carefully: only set if timestamp exists
- Date nfcCreationDate = null;
+ Date nfcCreationDate;
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
if (nfcCreationTimestamp != -1) {
nfcCreationDate = new Date(nfcCreationTimestamp);
+ } else {
+ nfcCreationDate = new Date();
}
+ CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate);
+ cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix!
+
// sign and encrypt
pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
.setSignatureMasterKeyId(signKeyId)
- .setNfcState(nfcSignedHash, nfcCreationDate)
+ .setCryptoInput(cryptoInput)
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
}
@@ -439,7 +450,7 @@ public class OpenPgpService extends RemoteService {
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
+ data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, 0L); // TODO fix
// return PendingIntent to be executed by client
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_INTENT,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
index d6da18e6e..485d5de72 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -26,31 +26,31 @@ import java.util.ArrayList;
import java.util.Date;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
-import org.sufficientlysecure.keychain.service.input.CryptoOperationParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
/**
* This class is a a transferable representation for a number of keyrings to
* be certified.
*/
-public class CertifyActionsParcel extends CryptoOperationParcel {
+public class CertifyActionsParcel implements Parcelable {
// the master key id to certify with
final public long mMasterKeyId;
public CertifyLevel mLevel;
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
+ public CryptoInputParcel mCryptoInput;
public CertifyActionsParcel(Date operationTime, long masterKeyId) {
- super(operationTime);
mMasterKeyId = masterKeyId;
+ mCryptoInput = new CryptoInputParcel(operationTime);
mLevel = CertifyLevel.DEFAULT;
}
public CertifyActionsParcel(Parcel source) {
- super(source);
-
mMasterKeyId = source.readLong();
+ mCryptoInput = source.readParcelable(CertifyActionsParcel.class.getClassLoader());
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
mLevel = CertifyLevel.values()[source.readInt()];
@@ -63,9 +63,8 @@ public class CertifyActionsParcel extends CryptoOperationParcel {
@Override
public void writeToParcel(Parcel destination, int flags) {
- super.writeToParcel(destination, flags);
-
destination.writeLong(mMasterKeyId);
+ destination.writeParcelable(mCryptoInput, 0);
destination.writeInt(mLevel.ordinal());
destination.writeSerializable(mCertifyActions);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
new file mode 100644
index 000000000..e02eda4b3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
@@ -0,0 +1,81 @@
+package org.sufficientlysecure.keychain.service.input;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+/** This is a base class for the input of crypto operations.
+ *
+ */
+public class CryptoInputParcel implements Parcelable {
+
+ Date mSignatureTime;
+
+ // this map contains both decrypted session keys and signed hashes to be
+ // used in the crypto operation described by this parcel.
+ private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>();
+
+ public CryptoInputParcel(Date signatureTime) {
+ mSignatureTime = signatureTime == null ? new Date() : signatureTime;
+ }
+
+ protected CryptoInputParcel(Parcel source) {
+ mSignatureTime = new Date(source.readLong());
+
+ {
+ int count = source.readInt();
+ mCryptoData = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ byte[] key = source.createByteArray();
+ byte[] value = source.createByteArray();
+ mCryptoData.put(ByteBuffer.wrap(key), value);
+ }
+ }
+
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mSignatureTime.getTime());
+
+ dest.writeInt(mCryptoData.size());
+ for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) {
+ dest.writeByteArray(entry.getKey().array());
+ dest.writeByteArray(entry.getValue());
+ }
+ }
+
+ public void addCryptoData(byte[] hash, byte[] signedHash) {
+ mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
+ }
+
+ public Map<ByteBuffer, byte[]> getCryptoData() {
+ return Collections.unmodifiableMap(mCryptoData);
+ }
+
+ public Date getSignatureTime() {
+ return mSignatureTime;
+ }
+
+ public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() {
+ public CryptoInputParcel createFromParcel(final Parcel source) {
+ return new CryptoInputParcel(source);
+ }
+
+ public CryptoInputParcel[] newArray(final int size) {
+ return new CryptoInputParcel[size];
+ }
+ };
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java
deleted file mode 100644
index 2101755ad..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.sufficientlysecure.keychain.service.input;
-
-import java.nio.ByteBuffer;
-import java.util.Date;
-import java.util.HashMap;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-
-/** This is a base class for the input of crypto operations.
- *
- */
-public abstract class CryptoOperationParcel implements Parcelable {
-
- Date mOperationTime;
-
- // this map contains both decrypted session keys and signed hashes to be
- // used in the crypto operation described by this parcel.
- HashMap<ByteBuffer,byte[]> mCryptoData;
-
- protected CryptoOperationParcel(Date operationTime) {
- mOperationTime = operationTime;
- }
-
- protected CryptoOperationParcel(Parcel source) {
- mOperationTime = new Date(source.readLong());
-
- {
- int count = source.readInt();
- mCryptoData = new HashMap<>(count);
- for (int i = 0; i < count; i++) {
- byte[] key = source.createByteArray();
- byte[] value = source.createByteArray();
- mCryptoData.put(ByteBuffer.wrap(key), value);
- }
- }
-
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mOperationTime.getTime());
-
- dest.writeInt(mCryptoData.size());
- for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) {
- dest.writeByteArray(entry.getKey().array());
- dest.writeByteArray(entry.getValue());
- }
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java
new file mode 100644
index 000000000..5d35e94e0
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java
@@ -0,0 +1,85 @@
+package org.sufficientlysecure.keychain.service.input;
+
+import java.util.Date;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class NfcOperationsParcel implements Parcelable {
+
+ public enum NfcOperationType {
+ NFC_SIGN, NFC_DECRYPT
+ }
+
+ public Date mSignatureTime;
+ public final NfcOperationType mType;
+ public final byte[][] mInputHash;
+ public final int[] mSignAlgo;
+
+ private NfcOperationsParcel(NfcOperationType type, byte[] inputHash, int signAlgo, Date signatureTime) {
+ mType = type;
+ mInputHash = new byte[][] { inputHash };
+ mSignAlgo = new int[] { signAlgo };
+ mSignatureTime = signatureTime;
+ }
+
+ public NfcOperationsParcel(Parcel source) {
+ mType = NfcOperationType.values()[source.readInt()];
+
+ {
+ int count = source.readInt();
+ mInputHash = new byte[count][];
+ mSignAlgo = new int[count];
+ for (int i = 0; i < count; i++) {
+ mInputHash[i] = source.createByteArray();
+ mSignAlgo[i] = source.readInt();
+ }
+ }
+
+ mSignatureTime = source.readInt() != 0 ? new Date(source.readLong()) : null;
+
+ }
+
+ public static NfcOperationsParcel createNfcSignOperation(
+ byte[] inputHash, int signAlgo, Date signatureTime) {
+ return new NfcOperationsParcel(NfcOperationType.NFC_SIGN, inputHash, signAlgo, signatureTime);
+ }
+
+ public static NfcOperationsParcel createNfcDecryptOperation(byte[] inputHash) {
+ return new NfcOperationsParcel(NfcOperationType.NFC_DECRYPT, inputHash, 0, null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType.ordinal());
+ dest.writeInt(mInputHash.length);
+ for (int i = 0; i < mInputHash.length; i++) {
+ dest.writeByteArray(mInputHash[i]);
+ dest.writeInt(mSignAlgo[i]);
+ }
+ if (mSignatureTime != null) {
+ dest.writeInt(1);
+ dest.writeLong(mSignatureTime.getTime());
+ } else {
+ dest.writeInt(0);
+ }
+
+ }
+
+ public static final Creator<NfcOperationsParcel> CREATOR = new Creator<NfcOperationsParcel>() {
+ public NfcOperationsParcel createFromParcel(final Parcel source) {
+ return new NfcOperationsParcel(source);
+ }
+
+ public NfcOperationsParcel[] newArray(final int size) {
+ return new NfcOperationsParcel[size];
+ }
+ };
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 35dfcb87c..75f22f01c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -25,15 +25,15 @@ import android.os.Message;
import android.os.Messenger;
import android.view.View;
-import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
-import java.util.Date;
public abstract class EncryptActivity extends BaseActivity {
@@ -42,8 +42,6 @@ public abstract class EncryptActivity extends BaseActivity {
// For NFC data
protected String mSigningKeyPassphrase = null;
- protected Date mNfcTimestamp = null;
- protected byte[] mNfcHash = null;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -64,17 +62,12 @@ public abstract class EncryptActivity extends BaseActivity {
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
}
- protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) {
- // build PendingIntent for Yubikey NFC operations
- Intent intent = new Intent(this, NfcActivity.class);
- intent.setAction(NfcActivity.ACTION_SIGN_HASH);
+ protected void startNfcSign(long keyId, String pin, NfcOperationsParcel nfcOps) {
- // pass params through to activity that it can be returned again later to repeat pgp operation
- intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService
- intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId);
- intent.putExtra(NfcActivity.EXTRA_PIN, pin);
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo);
+ Intent intent = new Intent(this, NfcOperationActivity.class);
+ intent.putExtra(NfcOperationActivity.EXTRA_PIN, pin);
+ intent.putExtra(NfcOperationActivity.EXTRA_NFC_OPS, nfcOps);
+ // TODO respect keyid(?)
startActivityForResult(intent, REQUEST_CODE_NFC);
}
@@ -93,8 +86,9 @@ public abstract class EncryptActivity extends BaseActivity {
case REQUEST_CODE_NFC: {
if (resultCode == RESULT_OK && data != null) {
- mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
- startEncrypt();
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
+ startEncrypt(cryptoInput);
return;
}
break;
@@ -108,6 +102,10 @@ public abstract class EncryptActivity extends BaseActivity {
}
public void startEncrypt() {
+ startEncrypt(null);
+ }
+
+ public void startEncrypt(CryptoInputParcel cryptoInput) {
if (!inputIsValid()) {
// Notify was created by inputIsValid.
return;
@@ -117,8 +115,13 @@ public abstract class EncryptActivity extends BaseActivity {
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
+ final SignEncryptParcel input = createEncryptBundle();
+ if (cryptoInput != null) {
+ input.setCryptoInput(cryptoInput);
+ }
+
Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle());
+ data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Message is received after encrypting is done in KeychainIntentService
@@ -141,9 +144,13 @@ public abstract class EncryptActivity extends BaseActivity {
} else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
PgpSignEncryptResult.RESULT_PENDING_NFC) {
- mNfcTimestamp = pgpResult.getNfcTimestamp();
- startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(),
- pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
+ NfcOperationsParcel parcel = NfcOperationsParcel.createNfcSignOperation(
+ pgpResult.getNfcHash(),
+ pgpResult.getNfcAlgo(),
+ input.getSignatureTime());
+ startNfcSign(pgpResult.getNfcKeyId(),
+ pgpResult.getNfcPassphrase(), parcel);
+
} else {
throw new RuntimeException("Unhandled pending result!");
}
@@ -158,8 +165,6 @@ public abstract class EncryptActivity extends BaseActivity {
// no matter the result, reset parameters
mSigningKeyPassphrase = null;
- mNfcHash = null;
- mNfcTimestamp = null;
}
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
index f1784fab3..49a0f5016 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
@@ -252,7 +252,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
data.setSignatureMasterKeyId(mSigningKeyId);
data.setSignaturePassphrase(mSigningKeyPassphrase);
- data.setNfcState(mNfcHash, mNfcTimestamp);
}
return data;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
index 14f2c492d..894c1202d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
@@ -232,7 +232,6 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
data.setSignatureMasterKeyId(mSigningKeyId);
data.setSignaturePassphrase(mSigningKeyPassphrase);
- data.setNfcState(mNfcHash, mNfcTimestamp);
}
return data;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
new file mode 100644
index 000000000..c1403d748
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
@@ -0,0 +1,492 @@
+/**
+ * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
+ *
+ * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.tech.IsoDep;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
+import org.sufficientlysecure.keychain.util.Iso7816TLV;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+
+/**
+ * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
+ * NFC devices.
+ *
+ * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
+ */
+@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
+public class NfcOperationActivity extends BaseActivity {
+
+ public static final String EXTRA_PIN = "pin";
+ public static final String EXTRA_NFC_OPS = "nfc_operations";
+
+ public static final String RESULT_DATA = "result_data";
+
+ private static final int TIMEOUT = 100000;
+
+ private NfcAdapter mNfcAdapter;
+ private IsoDep mIsoDep;
+
+ private String mPin;
+
+ NfcOperationsParcel mNfcOperations;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(Constants.TAG, "NfcOperationActivity.onCreate");
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
+ throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!");
+ }
+
+ Bundle data = intent.getExtras();
+
+ mNfcOperations = data.getParcelable(EXTRA_NFC_OPS);
+ mPin = data.getString(EXTRA_PIN);
+
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.nfc_activity);
+ }
+
+ /**
+ * Called when the system is about to start resuming a previous activity,
+ * disables NFC Foreground Dispatch
+ */
+ public void onPause() {
+ super.onPause();
+ Log.d(Constants.TAG, "NfcOperationActivity.onPause");
+
+ disableNfcForegroundDispatch();
+ }
+
+ /**
+ * Called when the activity will start interacting with the user,
+ * enables NFC Foreground Dispatch
+ */
+ public void onResume() {
+ super.onResume();
+ Log.d(Constants.TAG, "NfcOperationActivity.onResume");
+
+ enableNfcForegroundDispatch();
+ }
+
+ /**
+ * This activity is started as a singleTop activity.
+ * All new NFC Intents which are delivered to this activity are handled here
+ */
+ public void onNewIntent(Intent intent) {
+ if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
+ try {
+ handleNdefDiscoveredIntent(intent);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Connection error!", e);
+ toast("Connection Error: " + e.getMessage());
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ }
+
+ /** Handle NFC communication and return a result.
+ *
+ * This method is called by onNewIntent above upon discovery of an NFC tag.
+ * It handles initialization and login to the application, subsequently
+ * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
+ * finishes the activity with an appropiate result.
+ *
+ * On general communication, see also
+ * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
+ *
+ * References to pages are generally related to the OpenPGP Application
+ * on ISO SmartCard Systems specification.
+ *
+ */
+ private void handleNdefDiscoveredIntent(Intent intent) throws IOException {
+
+ Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+
+ // Connect to the detected tag, setting a couple of settings
+ mIsoDep = IsoDep.get(detectedTag);
+ mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation
+ mIsoDep.connect();
+
+ // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
+ // See specification, page 51
+ String accepted = "9000";
+
+ // Command APDU (page 51) for SELECT FILE command (page 29)
+ String opening =
+ "00" // CLA
+ + "A4" // INS
+ + "04" // P1
+ + "00" // P2
+ + "06" // Lc (number of bytes)
+ + "D27600012401" // Data (6 bytes)
+ + "00"; // Le
+ if ( ! card(opening).equals(accepted)) { // activate connection
+ toast("Opening Error!");
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ // Command APDU for VERIFY command (page 32)
+ String login =
+ "00" // CLA
+ + "20" // INS
+ + "00" // P1
+ + "82" // P2 (PW1)
+ + String.format("%02x", mPin.length()) // Lc
+ + Hex.toHexString(mPin.getBytes());
+ if ( ! card(login).equals(accepted)) { // login
+ toast("Wrong PIN!");
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ CryptoInputParcel resultData = new CryptoInputParcel(mNfcOperations.mSignatureTime);
+
+ switch (mNfcOperations.mType) {
+
+ case NFC_DECRYPT:
+
+ for (int i = 0; i < mNfcOperations.mInputHash.length; i++) {
+ byte[] hash = mNfcOperations.mInputHash[i];
+ byte[] decryptedSessionKey = nfcDecryptSessionKey(hash);
+ resultData.addCryptoData(hash, decryptedSessionKey);
+ }
+ break;
+
+ case NFC_SIGN:
+ for (int i = 0; i < mNfcOperations.mInputHash.length; i++) {
+ byte[] hash = mNfcOperations.mInputHash[i];
+ int algo = mNfcOperations.mSignAlgo[i];
+ byte[] signedHash = nfcCalculateSignature(hash, algo);
+ resultData.addCryptoData(hash, signedHash);
+ }
+ break;
+ }
+
+ // give data through for new service call
+ Intent result = new Intent();
+ result.putExtra(RESULT_DATA, resultData);
+ setResult(RESULT_OK, result);
+ finish();
+
+ }
+
+ /**
+ * Gets the user ID
+ *
+ * @return the user id as "name <email>"
+ * @throws java.io.IOException
+ */
+ public String getUserId() throws IOException {
+ String info = "00CA006500";
+ String data = "00CA005E00";
+ return getName(card(info)) + " <" + (new String(Hex.decode(getDataField(card(data))))) + ">";
+ }
+
+ /** Return the key id from application specific data stored on tag, or null
+ * if it doesn't exist.
+ *
+ * @param idx Index of the key to return the fingerprint from.
+ * @return The long key id of the requested key, or null if not found.
+ */
+ public static Long nfcGetKeyId(IsoDep isoDep, int idx) throws IOException {
+ byte[] fp = nfcGetFingerprint(isoDep, idx);
+ if (fp == null) {
+ return null;
+ }
+ ByteBuffer buf = ByteBuffer.wrap(fp);
+ // skip first 12 bytes of the fingerprint
+ buf.position(12);
+ // the last eight bytes are the key id (big endian, which is default order in ByteBuffer)
+ return buf.getLong();
+ }
+
+ /** Return fingerprints of all keys from application specific data stored
+ * on tag, or null if data not available.
+ *
+ * @return The fingerprints of all subkeys in a contiguous byte array.
+ */
+ public static byte[] nfcGetFingerprints(IsoDep isoDep) throws IOException {
+ String data = "00CA006E00";
+ byte[] buf = isoDep.transceive(Hex.decode(data));
+
+ Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true);
+ Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint());
+
+ Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5);
+ if (fptlv == null) {
+ return null;
+ }
+
+ return fptlv.mV;
+ }
+
+ /** Return the fingerprint from application specific data stored on tag, or
+ * null if it doesn't exist.
+ *
+ * @param idx Index of the key to return the fingerprint from.
+ * @return The fingerprint of the requested key, or null if not found.
+ */
+ public static byte[] nfcGetFingerprint(IsoDep isoDep, int idx) throws IOException {
+ byte[] data = nfcGetFingerprints(isoDep);
+
+ // return the master key fingerprint
+ ByteBuffer fpbuf = ByteBuffer.wrap(data);
+ byte[] fp = new byte[20];
+ fpbuf.position(idx*20);
+ fpbuf.get(fp, 0, 20);
+
+ return fp;
+ }
+
+ /**
+ * Calls to calculate the signature and returns the MPI value
+ *
+ * @param hash the hash for signing
+ * @return a big integer representing the MPI for the given hash
+ * @throws java.io.IOException
+ */
+ public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
+
+ // dsi, including Lc
+ String dsi;
+
+ Log.i(Constants.TAG, "Hash: " + hashAlgo);
+ switch (hashAlgo) {
+ case HashAlgorithmTags.SHA1:
+ if (hash.length != 20) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 10!");
+ }
+ dsi = "23" // Lc
+ + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
+ + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes
+ + "0605" + "2B0E03021A" // OID of SHA1
+ + "0500" // TLV coding of ZERO
+ + "0414" + getHex(hash); // 0x14 are 20 hash bytes
+ break;
+ case HashAlgorithmTags.RIPEMD160:
+ if (hash.length != 20) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 20!");
+ }
+ dsi = "233021300906052B2403020105000414" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA224:
+ if (hash.length != 28) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!");
+ }
+ dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA256:
+ if (hash.length != 32) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!");
+ }
+ dsi = "333031300D060960864801650304020105000420" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA384:
+ if (hash.length != 48) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!");
+ }
+ dsi = "433041300D060960864801650304020205000430" + getHex(hash);
+ break;
+ case HashAlgorithmTags.SHA512:
+ if (hash.length != 64) {
+ throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!");
+ }
+ dsi = "533051300D060960864801650304020305000440" + getHex(hash);
+ break;
+ default:
+ throw new RuntimeException("Not supported hash algo!");
+ }
+
+ // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
+ String apdu =
+ "002A9E9A" // CLA, INS, P1, P2
+ + dsi // digital signature input
+ + "00"; // Le
+
+ String response = card(apdu);
+
+ // split up response into signature and status
+ String status = response.substring(response.length()-4);
+ String signature = response.substring(0, response.length() - 4);
+
+ // while we are getting 0x61 status codes, retrieve more data
+ while (status.substring(0, 2).equals("61")) {
+ Log.d(Constants.TAG, "requesting more data, status " + status);
+ // Send GET RESPONSE command
+ response = card("00C00000" + status.substring(2));
+ status = response.substring(response.length()-4);
+ signature += response.substring(0, response.length()-4);
+ }
+
+ Log.d(Constants.TAG, "final response:" + status);
+
+ if ( ! status.equals("9000")) {
+ toast("Bad NFC response code: " + status);
+ return null;
+ }
+
+ // Make sure the signature we received is actually the expected number of bytes long!
+ if (signature.length() != 256 && signature.length() != 512) {
+ toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2);
+ return null;
+ }
+
+ return Hex.decode(signature);
+ }
+
+ /**
+ * Calls to calculate the signature and returns the MPI value
+ *
+ * @param encryptedSessionKey the encoded session key
+ * @return the decoded session key
+ * @throws java.io.IOException
+ */
+ public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
+ String firstApdu = "102a8086fe";
+ String secondApdu = "002a808603";
+ String le = "00";
+
+ byte[] one = new byte[254];
+ // leave out first byte:
+ System.arraycopy(encryptedSessionKey, 1, one, 0, one.length);
+
+ byte[] two = new byte[encryptedSessionKey.length - 1 - one.length];
+ for (int i = 0; i < two.length; i++) {
+ two[i] = encryptedSessionKey[i + one.length + 1];
+ }
+
+ String first = card(firstApdu + getHex(one));
+ String second = card(secondApdu + getHex(two) + le);
+
+ String decryptedSessionKey = getDataField(second);
+
+ Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey);
+
+ return Hex.decode(decryptedSessionKey);
+ }
+
+ /**
+ * Prints a message to the screen
+ *
+ * @param text the text which should be contained within the toast
+ */
+ private void toast(String text) {
+ Toast.makeText(this, text, Toast.LENGTH_LONG).show();
+ }
+
+ /**
+ * Receive new NFC Intents to this activity only by enabling foreground dispatch.
+ * This can only be done in onResume!
+ */
+ public void enableNfcForegroundDispatch() {
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ Intent nfcI = new Intent(this, NfcOperationActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT);
+ IntentFilter[] writeTagFilters = new IntentFilter[]{
+ new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
+ };
+
+ // https://code.google.com/p/android/issues/detail?id=62918
+ // maybe mNfcAdapter.enableReaderMode(); ?
+ try {
+ mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
+ } catch (IllegalStateException e) {
+ Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e);
+ }
+ Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!");
+ }
+
+ /**
+ * Disable foreground dispatch in onPause!
+ */
+ public void disableNfcForegroundDispatch() {
+ mNfcAdapter.disableForegroundDispatch(this);
+ Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!");
+ }
+
+ /**
+ * Gets the name of the user out of the raw card output regarding card holder related data
+ *
+ * @param name the raw card holder related data from the card
+ * @return the name given in this data
+ */
+ public String getName(String name) {
+ String slength;
+ int ilength;
+ name = name.substring(6);
+ slength = name.substring(0, 2);
+ ilength = Integer.parseInt(slength, 16) * 2;
+ name = name.substring(2, ilength + 2);
+ name = (new String(Hex.decode(name))).replace('<', ' ');
+ return (name);
+ }
+
+ /**
+ * Reduces the raw data from the card by four characters
+ *
+ * @param output the raw data from the card
+ * @return the data field of that data
+ */
+ private String getDataField(String output) {
+ return output.substring(0, output.length() - 4);
+ }
+
+ /**
+ * Communicates with the OpenPgpCard via the APDU
+ *
+ * @param hex the hexadecimal APDU
+ * @return The answer from the card
+ * @throws java.io.IOException throws an exception if something goes wrong
+ */
+ public String card(String hex) throws IOException {
+ return getHex(mIsoDep.transceive(Hex.decode(hex)));
+ }
+
+ /**
+ * Converts a byte array into an hex string
+ *
+ * @param raw the byte array representation
+ * @return the hexadecimal string representation
+ */
+ public static String getHex(byte[] raw) {
+ return new String(Hex.encode(raw));
+ }
+}