mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -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());
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||||
b.setSymmetricPassphrase(mPassphrase);
|
b.setSymmetricPassphrase(mPassphrase);
|
||||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ public class PgpEncryptDecryptTest {
|
|||||||
new ProviderHelper(Robolectric.application), null);
|
new ProviderHelper(Robolectric.application), null);
|
||||||
|
|
||||||
InputData data = new InputData(in, in.available());
|
InputData data = new InputData(in, in.available());
|
||||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||||
|
|
||||||
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||||
@ -301,7 +301,7 @@ public class PgpEncryptDecryptTest {
|
|||||||
|
|
||||||
InputData data = new InputData(in, in.available());
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||||
b.setEncryptionMasterKeyIds(new long[] {
|
b.setEncryptionMasterKeyIds(new long[] {
|
||||||
mStaticRing1.getMasterKeyId(),
|
mStaticRing1.getMasterKeyId(),
|
||||||
mStaticRing2.getMasterKeyId()
|
mStaticRing2.getMasterKeyId()
|
||||||
@ -393,7 +393,7 @@ public class PgpEncryptDecryptTest {
|
|||||||
new ProviderHelper(Robolectric.application), null);
|
new ProviderHelper(Robolectric.application), null);
|
||||||
|
|
||||||
InputData data = new InputData(in, in.available());
|
InputData data = new InputData(in, in.available());
|
||||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||||
|
|
||||||
b.setEncryptionMasterKeyIds(new long[] {
|
b.setEncryptionMasterKeyIds(new long[] {
|
||||||
mStaticRing1.getMasterKeyId(),
|
mStaticRing1.getMasterKeyId(),
|
||||||
@ -475,7 +475,7 @@ public class PgpEncryptDecryptTest {
|
|||||||
new ProviderHelper(Robolectric.application), null);
|
new ProviderHelper(Robolectric.application), null);
|
||||||
|
|
||||||
InputData data = new InputData(in, in.available());
|
InputData data = new InputData(in, in.available());
|
||||||
PgpSignEncryptInput b = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel b = new setSignatureTimestamp();
|
||||||
|
|
||||||
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||||
|
@ -680,6 +680,12 @@
|
|||||||
android:taskAffinity=":Nfc"
|
android:taskAffinity=":Nfc"
|
||||||
android:allowTaskReparenting="true" />
|
android:allowTaskReparenting="true" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.NfcOperationActivity"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=":Nfc"
|
||||||
|
android:allowTaskReparenting="true" />
|
||||||
|
|
||||||
<!--<activity-->
|
<!--<activity-->
|
||||||
<!--android:name=".ui.NfcIntentActivity"-->
|
<!--android:name=".ui.NfcIntentActivity"-->
|
||||||
<!--android:launchMode="singleTop">-->
|
<!--android:launchMode="singleTop">-->
|
||||||
|
@ -14,8 +14,12 @@ import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
|||||||
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
|
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is based on JcaPGPContentSignerBuilder.
|
* This class is based on JcaPGPContentSignerBuilder.
|
||||||
@ -31,31 +35,27 @@ public class NfcSyncPGPContentSignerBuilder
|
|||||||
private int keyAlgorithm;
|
private int keyAlgorithm;
|
||||||
private long keyID;
|
private long keyID;
|
||||||
|
|
||||||
private byte[] signedHash;
|
private Map signedHashes;
|
||||||
private Date creationTimestamp;
|
|
||||||
|
|
||||||
public static class NfcInteractionNeeded extends RuntimeException
|
public static class NfcInteractionNeeded extends RuntimeException
|
||||||
{
|
{
|
||||||
public byte[] hashToSign;
|
public byte[] hashToSign;
|
||||||
public Date creationTimestamp;
|
|
||||||
public int hashAlgo;
|
public int hashAlgo;
|
||||||
|
|
||||||
public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo, Date creationTimestamp)
|
public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo)
|
||||||
{
|
{
|
||||||
super("NFC interaction required!");
|
super("NFC interaction required!");
|
||||||
this.hashToSign = hashToSign;
|
this.hashToSign = hashToSign;
|
||||||
this.hashAlgo = hashAlgo;
|
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.keyAlgorithm = keyAlgorithm;
|
||||||
this.hashAlgorithm = hashAlgorithm;
|
this.hashAlgorithm = hashAlgorithm;
|
||||||
this.keyID = keyID;
|
this.keyID = keyID;
|
||||||
this.signedHash = signedHash;
|
this.signedHashes = signedHashes;
|
||||||
this.creationTimestamp = creationTimestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NfcSyncPGPContentSignerBuilder setProvider(Provider provider)
|
public NfcSyncPGPContentSignerBuilder setProvider(Provider provider)
|
||||||
@ -125,14 +125,14 @@ public class NfcSyncPGPContentSignerBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSignature() {
|
public byte[] getSignature() {
|
||||||
if (signedHash != null) {
|
byte[] digest = digestCalculator.getDigest();
|
||||||
// we already have the signed hash from a previous execution, return this!
|
ByteBuffer buf = ByteBuffer.wrap(digest);
|
||||||
return signedHash;
|
if (signedHashes.containsKey(buf)) {
|
||||||
} else {
|
return (byte[]) signedHashes.get(buf);
|
||||||
|
}
|
||||||
// catch this when signatureGenerator.generate() is executed and divert digest to card,
|
// catch this when signatureGenerator.generate() is executed and divert digest to card,
|
||||||
// when doing the operation again reuse creationTimestamp (this will be hashed)
|
// when doing the operation again reuse creationTimestamp (this will be hashed)
|
||||||
throw new NfcInteractionNeeded(digestCalculator.getDigest(), getHashAlgorithm(), creationTimestamp);
|
throw new NfcInteractionNeeded(digest, getHashAlgorithm());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getDigest()
|
public byte[] getDigest()
|
||||||
|
@ -35,7 +35,6 @@ public class PgpSignEncryptResult extends OperationResult {
|
|||||||
long mNfcKeyId;
|
long mNfcKeyId;
|
||||||
byte[] mNfcHash;
|
byte[] mNfcHash;
|
||||||
int mNfcAlgo;
|
int mNfcAlgo;
|
||||||
Date mNfcTimestamp;
|
|
||||||
String mNfcPassphrase;
|
String mNfcPassphrase;
|
||||||
byte[] mDetachedSignature;
|
byte[] mDetachedSignature;
|
||||||
|
|
||||||
@ -47,11 +46,10 @@ public class PgpSignEncryptResult extends OperationResult {
|
|||||||
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
|
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;
|
mNfcKeyId = nfcKeyId;
|
||||||
mNfcHash = nfcHash;
|
mNfcHash = nfcHash;
|
||||||
mNfcAlgo = nfcAlgo;
|
mNfcAlgo = nfcAlgo;
|
||||||
mNfcTimestamp = nfcTimestamp;
|
|
||||||
mNfcPassphrase = passphrase;
|
mNfcPassphrase = passphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,10 +69,6 @@ public class PgpSignEncryptResult extends OperationResult {
|
|||||||
return mNfcAlgo;
|
return mNfcAlgo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getNfcTimestamp() {
|
|
||||||
return mNfcTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNfcPassphrase() {
|
public String getNfcPassphrase() {
|
||||||
return mNfcPassphrase;
|
return mNfcPassphrase;
|
||||||
}
|
}
|
||||||
@ -95,7 +89,6 @@ public class PgpSignEncryptResult extends OperationResult {
|
|||||||
super(source);
|
super(source);
|
||||||
mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;
|
mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;
|
||||||
mNfcAlgo = source.readInt();
|
mNfcAlgo = source.readInt();
|
||||||
mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null;
|
|
||||||
mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null;
|
mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,12 +105,6 @@ public class PgpSignEncryptResult extends OperationResult {
|
|||||||
dest.writeInt(0);
|
dest.writeInt(0);
|
||||||
}
|
}
|
||||||
dest.writeInt(mNfcAlgo);
|
dest.writeInt(mNfcAlgo);
|
||||||
if (mNfcTimestamp != null) {
|
|
||||||
dest.writeInt(1);
|
|
||||||
dest.writeLong(mNfcTimestamp.getTime());
|
|
||||||
} else {
|
|
||||||
dest.writeInt(0);
|
|
||||||
}
|
|
||||||
if (mDetachedSignature != null) {
|
if (mDetachedSignature != null) {
|
||||||
dest.writeInt(1);
|
dest.writeInt(1);
|
||||||
dest.writeByteArray(mDetachedSignature);
|
dest.writeByteArray(mDetachedSignature);
|
||||||
|
@ -21,6 +21,9 @@ import android.os.Parcel;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;
|
||||||
|
|
||||||
|
|
||||||
public class SignEncryptResult extends OperationResult {
|
public class SignEncryptResult extends OperationResult {
|
||||||
|
|
||||||
ArrayList<PgpSignEncryptResult> mResults;
|
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.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for a PGPSecretKey.
|
* Wrapper for a PGPSecretKey.
|
||||||
@ -183,13 +186,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
return PgpConstants.sPreferredHashAlgorithms;
|
return PgpConstants.sPreferredHashAlgorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash,
|
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
|
||||||
Date nfcCreationTimestamp) {
|
Map<ByteBuffer,byte[]> signedHashes) {
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||||
// use synchronous "NFC based" SignerBuilder
|
// use synchronous "NFC based" SignerBuilder
|
||||||
return new NfcSyncPGPContentSignerBuilder(
|
return new NfcSyncPGPContentSignerBuilder(
|
||||||
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
|
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
|
||||||
mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp)
|
mSecretKey.getKeyID(), signedHashes)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
} else {
|
} else {
|
||||||
// content signer based on signing key algorithm and chosen hash algorithm
|
// 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,
|
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
|
||||||
byte[] nfcSignedHash, Date nfcCreationTimestamp)
|
Map<ByteBuffer,byte[]> signedHashes, Date creationTimestamp)
|
||||||
throws PgpGeneralException {
|
throws PgpGeneralException {
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
throw new PrivateKeyNotUnlockedException();
|
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.
|
// We explicitly create a signature creation timestamp in this place.
|
||||||
// That way, we can inject an artificial one from outside, ie the one
|
// That way, we can inject an artificial one from outside, ie the one
|
||||||
// used in previous runs of this function.
|
// used in previous runs of this function.
|
||||||
if (nfcCreationTimestamp == null) {
|
if (creationTimestamp == null) {
|
||||||
// to sign using nfc PgpSignEncrypt is executed two times.
|
// 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 first time it stops to return the PendingIntent for nfc connection and signing the hash
|
||||||
// the second time the signed hash is used.
|
// the second time the signed hash is used.
|
||||||
// to get the same hash we cache the timestamp for the second round!
|
// to get the same hash we cache the timestamp for the second round!
|
||||||
nfcCreationTimestamp = new Date();
|
creationTimestamp = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo,
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, signedHashes);
|
||||||
nfcSignedHash, nfcCreationTimestamp);
|
|
||||||
|
|
||||||
int signatureType;
|
int signatureType;
|
||||||
if (cleartext) {
|
if (cleartext) {
|
||||||
@ -237,7 +236,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
|
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
|
spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
|
||||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
spGen.setSignatureCreationTime(false, creationTimestamp);
|
||||||
signatureGenerator.setHashedSubpackets(spGen.generate());
|
signatureGenerator.setHashedSubpackets(spGen.generate());
|
||||||
return signatureGenerator;
|
return signatureGenerator;
|
||||||
} catch (PgpKeyNotFoundException | PGPException e) {
|
} catch (PgpKeyNotFoundException | PGPException e) {
|
||||||
@ -267,8 +266,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
* @param userIds User IDs to certify
|
* @param userIds User IDs to certify
|
||||||
* @return A keyring with added certifications
|
* @return A keyring with added certifications
|
||||||
*/
|
*/
|
||||||
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
|
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing,
|
||||||
byte[] nfcSignedHash, Date nfcCreationTimestamp) {
|
List<String> userIds,
|
||||||
|
HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
throw new PrivateKeyNotUnlockedException();
|
throw new PrivateKeyNotUnlockedException();
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
PGPSignatureGenerator signatureGenerator;
|
PGPSignatureGenerator signatureGenerator;
|
||||||
{
|
{
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
||||||
PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp);
|
PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
|
||||||
|
|
||||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
try {
|
try {
|
||||||
@ -296,9 +296,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
|
|
||||||
{ // supply signatureGenerator with a SubpacketVector
|
{ // supply signatureGenerator with a SubpacketVector
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
if (nfcCreationTimestamp != null) {
|
if (creationTimestamp != null) {
|
||||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
spGen.setSignatureCreationTime(false, creationTimestamp);
|
||||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
|
Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
|
||||||
}
|
}
|
||||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||||
signatureGenerator.setHashedSubpackets(packetVector);
|
signatureGenerator.setHashedSubpackets(packetVector);
|
||||||
@ -331,7 +331,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
* @return A keyring with added certifications
|
* @return A keyring with added certifications
|
||||||
*/
|
*/
|
||||||
public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing,
|
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) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
throw new PrivateKeyNotUnlockedException();
|
throw new PrivateKeyNotUnlockedException();
|
||||||
}
|
}
|
||||||
@ -346,7 +347,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
PGPSignatureGenerator signatureGenerator;
|
PGPSignatureGenerator signatureGenerator;
|
||||||
{
|
{
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
|
||||||
PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp);
|
PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
|
||||||
|
|
||||||
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
try {
|
try {
|
||||||
@ -359,9 +360,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
|
|
||||||
{ // supply signatureGenerator with a SubpacketVector
|
{ // supply signatureGenerator with a SubpacketVector
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
if (nfcCreationTimestamp != null) {
|
if (creationTimestamp != null) {
|
||||||
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
spGen.setSignatureCreationTime(false, creationTimestamp);
|
||||||
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
|
Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);
|
||||||
}
|
}
|
||||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||||
signatureGenerator.setHashedSubpackets(packetVector);
|
signatureGenerator.setHashedSubpackets(packetVector);
|
||||||
|
@ -20,10 +20,17 @@ package org.sufficientlysecure.keychain.pgp;
|
|||||||
|
|
||||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Date;
|
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 String mVersionHeader = null;
|
||||||
protected boolean mEnableAsciiArmorOutput = false;
|
protected boolean mEnableAsciiArmorOutput = false;
|
||||||
@ -36,13 +43,71 @@ public class PgpSignEncryptInput {
|
|||||||
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
|
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
|
||||||
protected String mSignaturePassphrase = null;
|
protected String mSignaturePassphrase = null;
|
||||||
protected long mAdditionalEncryptId = Constants.key.none;
|
protected long mAdditionalEncryptId = Constants.key.none;
|
||||||
protected byte[] mNfcSignedHash = null;
|
|
||||||
protected Date mNfcCreationTimestamp = null;
|
|
||||||
protected boolean mFailOnMissingEncryptionKeyIds = false;
|
protected boolean mFailOnMissingEncryptionKeyIds = false;
|
||||||
protected String mCharset;
|
protected String mCharset;
|
||||||
protected boolean mCleartextSignature;
|
protected boolean mCleartextSignature;
|
||||||
protected boolean mDetachedSignature = false;
|
protected boolean mDetachedSignature = false;
|
||||||
protected boolean mHiddenRecipients = 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() {
|
public String getCharset() {
|
||||||
return mCharset;
|
return mCharset;
|
||||||
@ -56,19 +121,11 @@ public class PgpSignEncryptInput {
|
|||||||
return mFailOnMissingEncryptionKeyIds;
|
return mFailOnMissingEncryptionKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getNfcCreationTimestamp() {
|
|
||||||
return mNfcCreationTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getNfcSignedHash() {
|
|
||||||
return mNfcSignedHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAdditionalEncryptId() {
|
public long getAdditionalEncryptId() {
|
||||||
return mAdditionalEncryptId;
|
return mAdditionalEncryptId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setAdditionalEncryptId(long additionalEncryptId) {
|
public PgpSignEncryptInputParcel setAdditionalEncryptId(long additionalEncryptId) {
|
||||||
mAdditionalEncryptId = additionalEncryptId;
|
mAdditionalEncryptId = additionalEncryptId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -77,7 +134,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSignaturePassphrase;
|
return mSignaturePassphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) {
|
public PgpSignEncryptInputParcel setSignaturePassphrase(String signaturePassphrase) {
|
||||||
mSignaturePassphrase = signaturePassphrase;
|
mSignaturePassphrase = signaturePassphrase;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -86,7 +143,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSignatureHashAlgorithm;
|
return mSignatureHashAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSignatureHashAlgorithm(int signatureHashAlgorithm) {
|
public PgpSignEncryptInputParcel setSignatureHashAlgorithm(int signatureHashAlgorithm) {
|
||||||
mSignatureHashAlgorithm = signatureHashAlgorithm;
|
mSignatureHashAlgorithm = signatureHashAlgorithm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -95,7 +152,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSignatureSubKeyId;
|
return mSignatureSubKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSignatureSubKeyId(long signatureSubKeyId) {
|
public PgpSignEncryptInputParcel setSignatureSubKeyId(long signatureSubKeyId) {
|
||||||
mSignatureSubKeyId = signatureSubKeyId;
|
mSignatureSubKeyId = signatureSubKeyId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -104,7 +161,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSignatureMasterKeyId;
|
return mSignatureMasterKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSignatureMasterKeyId(long signatureMasterKeyId) {
|
public PgpSignEncryptInputParcel setSignatureMasterKeyId(long signatureMasterKeyId) {
|
||||||
mSignatureMasterKeyId = signatureMasterKeyId;
|
mSignatureMasterKeyId = signatureMasterKeyId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -113,7 +170,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSymmetricEncryptionAlgorithm;
|
return mSymmetricEncryptionAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
|
public PgpSignEncryptInputParcel setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
|
||||||
mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
|
mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -122,7 +179,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mSymmetricPassphrase;
|
return mSymmetricPassphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) {
|
public PgpSignEncryptInputParcel setSymmetricPassphrase(String symmetricPassphrase) {
|
||||||
mSymmetricPassphrase = symmetricPassphrase;
|
mSymmetricPassphrase = symmetricPassphrase;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -131,7 +188,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mEncryptionMasterKeyIds;
|
return mEncryptionMasterKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
|
public PgpSignEncryptInputParcel setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
|
||||||
mEncryptionMasterKeyIds = encryptionMasterKeyIds;
|
mEncryptionMasterKeyIds = encryptionMasterKeyIds;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -140,7 +197,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mCompressionId;
|
return mCompressionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setCompressionId(int compressionId) {
|
public PgpSignEncryptInputParcel setCompressionId(int compressionId) {
|
||||||
mCompressionId = compressionId;
|
mCompressionId = compressionId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -153,28 +210,22 @@ public class PgpSignEncryptInput {
|
|||||||
return mVersionHeader;
|
return mVersionHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setVersionHeader(String versionHeader) {
|
public PgpSignEncryptInputParcel setVersionHeader(String versionHeader) {
|
||||||
mVersionHeader = versionHeader;
|
mVersionHeader = versionHeader;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
|
public PgpSignEncryptInputParcel setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
|
||||||
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
|
mEnableAsciiArmorOutput = enableAsciiArmorOutput;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
|
public PgpSignEncryptInputParcel setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
|
||||||
mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
|
mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setNfcState(byte[] signedHash, Date creationTimestamp) {
|
public PgpSignEncryptInputParcel setCleartextSignature(boolean cleartextSignature) {
|
||||||
mNfcSignedHash = signedHash;
|
|
||||||
mNfcCreationTimestamp = creationTimestamp;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PgpSignEncryptInput setCleartextSignature(boolean cleartextSignature) {
|
|
||||||
this.mCleartextSignature = cleartextSignature;
|
this.mCleartextSignature = cleartextSignature;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -183,7 +234,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mCleartextSignature;
|
return mCleartextSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setDetachedSignature(boolean detachedSignature) {
|
public PgpSignEncryptInputParcel setDetachedSignature(boolean detachedSignature) {
|
||||||
this.mDetachedSignature = detachedSignature;
|
this.mDetachedSignature = detachedSignature;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -192,7 +243,7 @@ public class PgpSignEncryptInput {
|
|||||||
return mDetachedSignature;
|
return mDetachedSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) {
|
public PgpSignEncryptInputParcel setHiddenRecipients(boolean hiddenRecipients) {
|
||||||
this.mHiddenRecipients = hiddenRecipients;
|
this.mHiddenRecipients = hiddenRecipients;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -200,5 +251,29 @@ public class PgpSignEncryptInput {
|
|||||||
public boolean isHiddenRecipients() {
|
public boolean isHiddenRecipients() {
|
||||||
return mHiddenRecipients;
|
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.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
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.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
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.
|
* 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.results.PgpSignEncryptResult
|
||||||
* @see org.sufficientlysecure.keychain.operations.SignEncryptOperation
|
* @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
|
* Signs and/or encrypts data based on parameters of class
|
||||||
*/
|
*/
|
||||||
public PgpSignEncryptResult execute(PgpSignEncryptInput input,
|
public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input,
|
||||||
InputData inputData, OutputStream outputStream) {
|
InputData inputData, OutputStream outputStream) {
|
||||||
|
|
||||||
int indent = 0;
|
int indent = 0;
|
||||||
@ -282,7 +284,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
|||||||
try {
|
try {
|
||||||
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
|
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
|
||||||
signatureGenerator = signingKey.getSignatureGenerator(
|
signatureGenerator = signingKey.getSignatureGenerator(
|
||||||
input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
|
input.getSignatureHashAlgorithm(), cleartext,
|
||||||
|
input.getCryptoData(), input.getSignatureTime());
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
log.add(LogType.MSG_PSE_ERROR_NFC, indent);
|
log.add(LogType.MSG_PSE_ERROR_NFC, indent);
|
||||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||||
@ -489,7 +492,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
|||||||
new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);
|
new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);
|
||||||
// Note that the checked key here is the master key, not the signing key
|
// Note that the checked key here is the master key, not the signing key
|
||||||
// (although these are always the same on Yubikeys)
|
// (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));
|
Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.pgp;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
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.
|
* 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> mInputUris = new ArrayList<>();
|
||||||
public ArrayList<Uri> mOutputUris = new ArrayList<>();
|
public ArrayList<Uri> mOutputUris = new ArrayList<>();
|
||||||
@ -51,26 +50,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SignEncryptParcel(Parcel src) {
|
public SignEncryptParcel(Parcel src) {
|
||||||
|
super(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;
|
|
||||||
|
|
||||||
mInputUris = src.createTypedArrayList(Uri.CREATOR);
|
mInputUris = src.createTypedArrayList(Uri.CREATOR);
|
||||||
mOutputUris = 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) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(mVersionHeader);
|
super.writeToParcel(dest, flags);
|
||||||
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);
|
|
||||||
|
|
||||||
dest.writeTypedList(mInputUris);
|
dest.writeTypedList(mInputUris);
|
||||||
dest.writeTypedList(mOutputUris);
|
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.operations.results.PgpSignEncryptResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
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.PgpSignEncryptOperation;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
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.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
|
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
|
||||||
import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity;
|
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.ImportKeysActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.NfcActivity;
|
import org.sufficientlysecure.keychain.ui.NfcActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||||
@ -258,11 +259,13 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// carefully: only set if timestamp exists
|
// carefully: only set if timestamp exists
|
||||||
Date nfcCreationDate = null;
|
Date nfcCreationDate;
|
||||||
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
|
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
|
||||||
Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);
|
Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);
|
||||||
if (nfcCreationTimestamp != -1) {
|
if (nfcCreationTimestamp != -1) {
|
||||||
nfcCreationDate = new Date(nfcCreationTimestamp);
|
nfcCreationDate = new Date(nfcCreationTimestamp);
|
||||||
|
} else {
|
||||||
|
nfcCreationDate = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Input- and OutputStream from ParcelFileDescriptor
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
@ -275,15 +278,18 @@ public class OpenPgpService extends RemoteService {
|
|||||||
long inputLength = is.available();
|
long inputLength = is.available();
|
||||||
InputData inputData = new InputData(is, inputLength);
|
InputData inputData = new InputData(is, inputLength);
|
||||||
|
|
||||||
|
CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate);
|
||||||
|
cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix
|
||||||
|
|
||||||
// sign-only
|
// sign-only
|
||||||
PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
|
PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel()
|
||||||
.setEnableAsciiArmorOutput(asciiArmor)
|
.setEnableAsciiArmorOutput(asciiArmor)
|
||||||
.setCleartextSignature(cleartextSign)
|
.setCleartextSignature(cleartextSign)
|
||||||
.setDetachedSignature(!cleartextSign)
|
.setDetachedSignature(!cleartextSign)
|
||||||
.setVersionHeader(null)
|
.setVersionHeader(null)
|
||||||
.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
||||||
.setSignatureMasterKeyId(signKeyId)
|
.setSignatureMasterKeyId(signKeyId)
|
||||||
.setNfcState(nfcSignedHash, nfcCreationDate);
|
.setCryptoInput(cryptoInput);
|
||||||
|
|
||||||
// execute PGP operation!
|
// execute PGP operation!
|
||||||
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
|
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
|
||||||
@ -298,7 +304,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
// return PendingIntent to execute NFC activity
|
// return PendingIntent to execute NFC activity
|
||||||
// pass through the signature creation timestamp to be used again on second execution
|
// pass through the signature creation timestamp to be used again on second execution
|
||||||
// of PgpSignEncrypt when we have the signed hash!
|
// 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
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
@ -389,7 +395,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
long inputLength = is.available();
|
long inputLength = is.available();
|
||||||
InputData inputData = new InputData(is, inputLength, originalFilename);
|
InputData inputData = new InputData(is, inputLength, originalFilename);
|
||||||
|
|
||||||
PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
|
PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();
|
||||||
pseInput.setEnableAsciiArmorOutput(asciiArmor)
|
pseInput.setEnableAsciiArmorOutput(asciiArmor)
|
||||||
.setVersionHeader(null)
|
.setVersionHeader(null)
|
||||||
.setCompressionId(compressionId)
|
.setCompressionId(compressionId)
|
||||||
@ -412,16 +418,21 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
|
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
|
||||||
// carefully: only set if timestamp exists
|
// carefully: only set if timestamp exists
|
||||||
Date nfcCreationDate = null;
|
Date nfcCreationDate;
|
||||||
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
|
long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
|
||||||
if (nfcCreationTimestamp != -1) {
|
if (nfcCreationTimestamp != -1) {
|
||||||
nfcCreationDate = new Date(nfcCreationTimestamp);
|
nfcCreationDate = new Date(nfcCreationTimestamp);
|
||||||
|
} else {
|
||||||
|
nfcCreationDate = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate);
|
||||||
|
cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix!
|
||||||
|
|
||||||
// sign and encrypt
|
// sign and encrypt
|
||||||
pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
|
||||||
.setSignatureMasterKeyId(signKeyId)
|
.setSignatureMasterKeyId(signKeyId)
|
||||||
.setNfcState(nfcSignedHash, nfcCreationDate)
|
.setCryptoInput(cryptoInput)
|
||||||
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
|
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +450,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
// return PendingIntent to execute NFC activity
|
// return PendingIntent to execute NFC activity
|
||||||
// pass through the signature creation timestamp to be used again on second execution
|
// pass through the signature creation timestamp to be used again on second execution
|
||||||
// of PgpSignEncrypt when we have the signed hash!
|
// 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
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_INTENT,
|
result.putExtra(OpenPgpApi.RESULT_INTENT,
|
||||||
|
@ -26,31 +26,31 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
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
|
* This class is a a transferable representation for a number of keyrings to
|
||||||
* be certified.
|
* be certified.
|
||||||
*/
|
*/
|
||||||
public class CertifyActionsParcel extends CryptoOperationParcel {
|
public class CertifyActionsParcel implements Parcelable {
|
||||||
|
|
||||||
// the master key id to certify with
|
// the master key id to certify with
|
||||||
final public long mMasterKeyId;
|
final public long mMasterKeyId;
|
||||||
public CertifyLevel mLevel;
|
public CertifyLevel mLevel;
|
||||||
|
|
||||||
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
|
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
|
||||||
|
public CryptoInputParcel mCryptoInput;
|
||||||
|
|
||||||
public CertifyActionsParcel(Date operationTime, long masterKeyId) {
|
public CertifyActionsParcel(Date operationTime, long masterKeyId) {
|
||||||
super(operationTime);
|
|
||||||
mMasterKeyId = masterKeyId;
|
mMasterKeyId = masterKeyId;
|
||||||
|
mCryptoInput = new CryptoInputParcel(operationTime);
|
||||||
mLevel = CertifyLevel.DEFAULT;
|
mLevel = CertifyLevel.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CertifyActionsParcel(Parcel source) {
|
public CertifyActionsParcel(Parcel source) {
|
||||||
super(source);
|
|
||||||
|
|
||||||
mMasterKeyId = source.readLong();
|
mMasterKeyId = source.readLong();
|
||||||
|
mCryptoInput = source.readParcelable(CertifyActionsParcel.class.getClassLoader());
|
||||||
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
|
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
|
||||||
mLevel = CertifyLevel.values()[source.readInt()];
|
mLevel = CertifyLevel.values()[source.readInt()];
|
||||||
|
|
||||||
@ -63,9 +63,8 @@ public class CertifyActionsParcel extends CryptoOperationParcel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel destination, int flags) {
|
public void writeToParcel(Parcel destination, int flags) {
|
||||||
super.writeToParcel(destination, flags);
|
|
||||||
|
|
||||||
destination.writeLong(mMasterKeyId);
|
destination.writeLong(mMasterKeyId);
|
||||||
|
destination.writeParcelable(mCryptoInput, 0);
|
||||||
destination.writeInt(mLevel.ordinal());
|
destination.writeInt(mLevel.ordinal());
|
||||||
|
|
||||||
destination.writeSerializable(mCertifyActions);
|
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.os.Messenger;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
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 {
|
public abstract class EncryptActivity extends BaseActivity {
|
||||||
|
|
||||||
@ -42,8 +42,6 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
// For NFC data
|
// For NFC data
|
||||||
protected String mSigningKeyPassphrase = null;
|
protected String mSigningKeyPassphrase = null;
|
||||||
protected Date mNfcTimestamp = null;
|
|
||||||
protected byte[] mNfcHash = null;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -64,17 +62,12 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) {
|
protected void startNfcSign(long keyId, String pin, NfcOperationsParcel nfcOps) {
|
||||||
// build PendingIntent for Yubikey NFC operations
|
|
||||||
Intent intent = new Intent(this, NfcActivity.class);
|
|
||||||
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
|
|
||||||
|
|
||||||
// pass params through to activity that it can be returned again later to repeat pgp operation
|
Intent intent = new Intent(this, NfcOperationActivity.class);
|
||||||
intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService
|
intent.putExtra(NfcOperationActivity.EXTRA_PIN, pin);
|
||||||
intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId);
|
intent.putExtra(NfcOperationActivity.EXTRA_NFC_OPS, nfcOps);
|
||||||
intent.putExtra(NfcActivity.EXTRA_PIN, pin);
|
// TODO respect keyid(?)
|
||||||
intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
|
|
||||||
intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo);
|
|
||||||
|
|
||||||
startActivityForResult(intent, REQUEST_CODE_NFC);
|
startActivityForResult(intent, REQUEST_CODE_NFC);
|
||||||
}
|
}
|
||||||
@ -93,8 +86,9 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
case REQUEST_CODE_NFC: {
|
case REQUEST_CODE_NFC: {
|
||||||
if (resultCode == RESULT_OK && data != null) {
|
if (resultCode == RESULT_OK && data != null) {
|
||||||
mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
|
CryptoInputParcel cryptoInput =
|
||||||
startEncrypt();
|
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
|
||||||
|
startEncrypt(cryptoInput);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -108,6 +102,10 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startEncrypt() {
|
public void startEncrypt() {
|
||||||
|
startEncrypt(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startEncrypt(CryptoInputParcel cryptoInput) {
|
||||||
if (!inputIsValid()) {
|
if (!inputIsValid()) {
|
||||||
// Notify was created by inputIsValid.
|
// Notify was created by inputIsValid.
|
||||||
return;
|
return;
|
||||||
@ -117,8 +115,13 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
Intent intent = new Intent(this, KeychainIntentService.class);
|
Intent intent = new Intent(this, KeychainIntentService.class);
|
||||||
intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
|
intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
|
||||||
|
|
||||||
|
final SignEncryptParcel input = createEncryptBundle();
|
||||||
|
if (cryptoInput != null) {
|
||||||
|
input.setCryptoInput(cryptoInput);
|
||||||
|
}
|
||||||
|
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle());
|
data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after encrypting is done in KeychainIntentService
|
// 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) ==
|
} else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
|
||||||
PgpSignEncryptResult.RESULT_PENDING_NFC) {
|
PgpSignEncryptResult.RESULT_PENDING_NFC) {
|
||||||
|
|
||||||
mNfcTimestamp = pgpResult.getNfcTimestamp();
|
NfcOperationsParcel parcel = NfcOperationsParcel.createNfcSignOperation(
|
||||||
startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(),
|
pgpResult.getNfcHash(),
|
||||||
pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
|
pgpResult.getNfcAlgo(),
|
||||||
|
input.getSignatureTime());
|
||||||
|
startNfcSign(pgpResult.getNfcKeyId(),
|
||||||
|
pgpResult.getNfcPassphrase(), parcel);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Unhandled pending result!");
|
throw new RuntimeException("Unhandled pending result!");
|
||||||
}
|
}
|
||||||
@ -158,8 +165,6 @@ public abstract class EncryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
// no matter the result, reset parameters
|
// no matter the result, reset parameters
|
||||||
mSigningKeyPassphrase = null;
|
mSigningKeyPassphrase = null;
|
||||||
mNfcHash = null;
|
|
||||||
mNfcTimestamp = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -252,7 +252,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
|||||||
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
|
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
|
||||||
data.setSignatureMasterKeyId(mSigningKeyId);
|
data.setSignatureMasterKeyId(mSigningKeyId);
|
||||||
data.setSignaturePassphrase(mSigningKeyPassphrase);
|
data.setSignaturePassphrase(mSigningKeyPassphrase);
|
||||||
data.setNfcState(mNfcHash, mNfcTimestamp);
|
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,6 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
|||||||
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
|
data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
|
||||||
data.setSignatureMasterKeyId(mSigningKeyId);
|
data.setSignatureMasterKeyId(mSigningKeyId);
|
||||||
data.setSignaturePassphrase(mSigningKeyPassphrase);
|
data.setSignaturePassphrase(mSigningKeyPassphrase);
|
||||||
data.setNfcState(mNfcHash, mNfcTimestamp);
|
|
||||||
}
|
}
|
||||||
return data;
|
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