mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
generalize nfc crypto input structure
This commit is contained in:
parent
4499caef1e
commit
aca54e31ea
@ -136,7 +136,7 @@ public class PgpEncryptDecryptTest {
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
|
||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
||||
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||
b.setSymmetricPassphrase(mPassphrase);
|
||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||
|
||||
@ -220,7 +220,7 @@ public class PgpEncryptDecryptTest {
|
||||
new ProviderHelper(Robolectric.application), null);
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
||||
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||
|
||||
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||
@ -301,7 +301,7 @@ public class PgpEncryptDecryptTest {
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
|
||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
||||
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||
b.setEncryptionMasterKeyIds(new long[] {
|
||||
mStaticRing1.getMasterKeyId(),
|
||||
mStaticRing2.getMasterKeyId()
|
||||
@ -393,7 +393,7 @@ public class PgpEncryptDecryptTest {
|
||||
new ProviderHelper(Robolectric.application), null);
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
||||
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||
|
||||
b.setEncryptionMasterKeyIds(new long[] {
|
||||
mStaticRing1.getMasterKeyId(),
|
||||
@ -475,7 +475,7 @@ public class PgpEncryptDecryptTest {
|
||||
new ProviderHelper(Robolectric.application), null);
|
||||
|
||||
InputData data = new InputData(in, in.available());
|
||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
||||
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||
|
||||
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||
|
@ -680,6 +680,12 @@
|
||||
android:taskAffinity=":Nfc"
|
||||
android:allowTaskReparenting="true" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.NfcOperationActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=":Nfc"
|
||||
android:allowTaskReparenting="true" />
|
||||
|
||||
<!--<activity-->
|
||||
<!--android:name=".ui.NfcIntentActivity"-->
|
||||
<!--android:launchMode="singleTop">-->
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user