aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org
diff options
context:
space:
mode:
authorhsm <hsm@lamia.panaceas.james.local>2016-05-29 03:12:09 +0100
committerhsm <hsm@lamia.panaceas.james.local>2016-05-29 04:47:15 +0100
commit5aa36b089f09346787e44f9850924faae1fda363 (patch)
tree0f14297dedecb42af771743241a0ed7ee78f308c /OpenKeychain/src/main/java/org
parent24b92172be818c7ee67a235ad8403f01a4495987 (diff)
downloadopen-keychain-master.tar.gz
open-keychain-master.tar.bz2
open-keychain-master.zip
ssh authentication supportHEADmaster
Diffstat (limited to 'OpenKeychain/src/main/java/org')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java79
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java12
7 files changed, 152 insertions, 24 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java
index c4e569d24..5d904331e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java
@@ -30,6 +30,7 @@ public class PgpSignEncryptData implements Parcelable {
protected String mVersionHeader = null;
protected boolean mEnableAsciiArmorOutput = false;
+ protected boolean mSshAuth = false;
protected int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
protected long[] mEncryptionMasterKeyIds = null;
protected Passphrase mSymmetricPassphrase = null;
@@ -53,6 +54,7 @@ public class PgpSignEncryptData implements Parcelable {
mVersionHeader = source.readString();
mEnableAsciiArmorOutput = source.readInt() == 1;
+ mSshAuth = source.readInt() == 1;
mCompressionAlgorithm = source.readInt();
mEncryptionMasterKeyIds = source.createLongArray();
mSymmetricPassphrase = source.readParcelable(loader);
@@ -78,6 +80,7 @@ public class PgpSignEncryptData implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mVersionHeader);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
+ dest.writeInt(mSshAuth ? 1 : 0);
dest.writeInt(mCompressionAlgorithm);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeParcelable(mSymmetricPassphrase, 0);
@@ -183,6 +186,10 @@ public class PgpSignEncryptData implements Parcelable {
return mEnableAsciiArmorOutput;
}
+ public boolean isSshAuth() {
+ return mSshAuth;
+ }
+
public String getVersionHeader() {
return mVersionHeader;
}
@@ -197,6 +204,11 @@ public class PgpSignEncryptData implements Parcelable {
return this;
}
+ public PgpSignEncryptData setSshAuth(boolean sshAuth) {
+ mSshAuth = sshAuth;
+ return this;
+ }
+
public PgpSignEncryptData setCleartextSignature(boolean cleartextSignature) {
this.mCleartextSignature = cleartextSignature;
return this;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 7a1d99927..a855dcac8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Parcelable;
import android.support.annotation.NonNull;
+import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
@@ -58,6 +59,10 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import java.nio.ByteBuffer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
@@ -165,6 +170,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
return executeInternal(input, cryptoInput, inputData, outputStream);
}
+
/**
* Signs and/or encrypts data based on parameters of class
*/
@@ -238,7 +244,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
}
// Make sure we are allowed to sign here!
- if (!signingKey.canSign()) {
+ if ((!signingKey.canSign() && !data.isSshAuth()) ||
+ (!signingKey.canAuthenticate() && data.isSshAuth())) {
+ Log.w(Constants.TAG, "canSign " + signingKey.canSign() + " canAuthenticate "
+ + signingKey.canAuthenticate() + " isSshAuth "+ data.isSshAuth());
log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent);
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
@@ -298,7 +307,11 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
// Use requested hash algo
int requestedAlgorithm = data.getSignatureHashAlgorithm();
if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
- data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
+ if (data.isSshAuth()) {
+ data.setSignatureHashAlgorithm(HashAlgorithmTags.SHA1);
+ } else {
+ data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
+ }
}
}
updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -386,6 +399,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
ByteArrayOutputStream detachedByteOut = null;
ArmoredOutputStream detachedArmorOut = null;
BCPGOutputStream detachedBcpgOut = null;
+ MessageDigest raw_digest = null;
long opTime, startTime = System.currentTimeMillis();
@@ -491,6 +505,28 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
} else if (enableSignature && data.isDetachedSignature()) {
/* detached signature */
+ // JMM - GROSS HACK
+ //
+ // The PGP stack doesn't do signatures of arbitary data: it wants
+ // to use its own structure for the message, so we have
+ // to be creative. We let the pgp stack do its stuff, but when
+ // it calls out to the NFC stack, we catch it and swap the
+ // message digest for own own.
+ //
+ // this means we have to check cyrptoInput for the correct
+ // hash ourselves on the 2nd trip through when the
+ // client resents the request.
+ //
+
+ if (data.isSshAuth()) {
+ try {
+ raw_digest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ log.add(LogType.MSG_PSE_ERROR_IO, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ }
+ }
+
updateProgress(R.string.progress_signing, 8, 100);
log.add(LogType.MSG_PSE_SIGNING_DETACHED, indent);
@@ -515,6 +551,9 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
while ((length = in.read(buffer)) > 0) {
// no output stream is written, no changed to original data!
+ if (raw_digest != null)
+ raw_digest.update(buffer, 0, length);
+
signatureGenerator.update(buffer, 0, length);
alreadyWritten += length;
@@ -582,11 +621,37 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
signatureGenerator.generate().encode(pOut);
}
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
- // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
- log.add(LogType.MSG_PSE_PENDING_NFC, indent);
- return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation(
- signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(),
- e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput);
+
+ // JMM - 2nd part of gross hack
+ // first swap the message digest for the raw one
+ // before this hits the NFC stack
+
+ if (raw_digest != null)
+ e.hashToSign = raw_digest.digest();
+
+ // Test to see if we already have the answer for the hash
+ // as the test in encode() above was checking the wrong
+ // hash.
+
+ byte [] answer = cryptoInput.getCryptoData().get(ByteBuffer.wrap(e.hashToSign));
+
+ if (raw_digest == null || answer == null) {
+ // We haven't done the hash yet, so kick it off to the NFC stack
+
+ // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
+
+ log.add(LogType.MSG_PSE_PENDING_NFC, indent);
+ return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation(
+ signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(),
+ e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput);
+ } else if (raw_digest != null) {
+ // We've aready done the work, replace the output stream
+ // from the PGP signature with just the signature as we want just
+ // a raw signature
+
+ detachedByteOut = new ByteArrayOutputStream();
+ detachedByteOut.write(answer, 0, answer.length);
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index 604a5a027..58c3a154e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -167,6 +167,23 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ /** Returns the key id which should be used for auth.
+ *
+ * This method returns keys which are actually available (ie. secret available, and not stripped,
+ * revoked, or expired), hence only works on keyrings where a secret key is available!
+ *
+ */
+ public long getSecretAuthId() throws PgpKeyNotFoundException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeyRings.HAS_AUTHENTICATE,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpKeyNotFoundException(e);
+ }
+ }
+
@Override
public int getVerified() throws PgpKeyNotFoundException {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 8a5d09d7b..8f3e4c247 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -336,6 +336,8 @@ public class KeychainProvider extends ContentProvider {
"kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
"kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
+ projectionMap.put(KeyRings.HAS_AUTHENTICATE,
+ "kA." + Keys.KEY_ID + " AS " + KeyRings.HAS_AUTHENTICATE);
projectionMap.put(KeyRings.HAS_CERTIFY,
"kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_CERTIFY);
projectionMap.put(KeyRings.IS_EXPIRED,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index c85774ead..c5cbcbb71 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -199,7 +199,8 @@ public class OpenPgpService extends Service {
}
private Intent signImpl(Intent data, InputStream inputStream,
- OutputStream outputStream, boolean cleartextSign) {
+ OutputStream outputStream, boolean cleartextSign,
+ boolean sshAuth) {
try {
boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -209,6 +210,7 @@ public class OpenPgpService extends Service {
.setCleartextSignature(cleartextSign)
.setDetachedSignature(!cleartextSign)
.setVersionHeader(null)
+ .setSshAuth(sshAuth)
.setSignatureHashAlgorithm(PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
@@ -227,9 +229,15 @@ public class OpenPgpService extends Service {
// get first usable subkey capable of signing
try {
- long signSubKeyId = mProviderHelper.getCachedPublicKeyRing(
+ long subKeyId;
+ if (sshAuth) {
+ subKeyId = mProviderHelper.getCachedPublicKeyRing(
+ pgpData.getSignatureMasterKeyId()).getSecretAuthId();
+ } else {
+ subKeyId = mProviderHelper.getCachedPublicKeyRing(
pgpData.getSignatureMasterKeyId()).getSecretSignId();
- pgpData.setSignatureSubKeyId(signSubKeyId);
+ }
+ pgpData.setSignatureSubKeyId(subKeyId);
} catch (PgpKeyNotFoundException e) {
throw new Exception("signing subkey not found!", e);
}
@@ -941,15 +949,18 @@ public class OpenPgpService extends Service {
return checkPermissionImpl(data);
}
case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
- return signImpl(data, inputStream, outputStream, true);
+ return signImpl(data, inputStream, outputStream, true, false);
}
case OpenPgpApi.ACTION_SIGN: {
// DEPRECATED: same as ACTION_CLEARTEXT_SIGN
Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
- return signImpl(data, inputStream, outputStream, true);
+ return signImpl(data, inputStream, outputStream, true, false);
}
case OpenPgpApi.ACTION_DETACHED_SIGN: {
- return signImpl(data, inputStream, outputStream, false);
+ return signImpl(data, inputStream, outputStream, false, false);
+ }
+ case OpenPgpApi.ACTION_SSH_AUTH: {
+ return signImpl(data, inputStream, outputStream, false, true);
}
case OpenPgpApi.ACTION_ENCRYPT: {
return encryptAndSignImpl(data, inputStream, outputStream, false);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
index bc1c42f7f..deee6366f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
@@ -513,9 +513,11 @@ public class SecurityTokenHelper {
* @param hash the hash for signing
* @return a big integer representing the MPI for the given hash
*/
- public byte[] calculateSignature(byte[] hash, int hashAlgo) throws IOException {
+ public byte[] calculateSignature(byte[] hash, int hashAlgo, boolean useAuthKey) throws IOException {
if (!mPw1ValidatedForSignature) {
- verifyPin(0x81); // (Verify PW1 with mode 81 for signing)
+ // (Verify PW1 with mode 81 for signing)
+ // (Verify PW1 with mode 82 for auth)
+ verifyPin(useAuthKey ? 0x82 : 0x81);
}
// dsi, including Lc
@@ -525,7 +527,7 @@ public class SecurityTokenHelper {
switch (hashAlgo) {
case HashAlgorithmTags.SHA1:
if (hash.length != 20) {
- throw new IOException("Bad hash length (" + hash.length + ", expected 10!");
+ throw new IOException("Bad hash length (" + hash.length + ", expected 20!");
}
dsi = "23" // Lc
+ "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
@@ -568,11 +570,24 @@ public class SecurityTokenHelper {
throw new IOException("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 apdu;
+
+ if (!useAuthKey) {
+ // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
+ apdu =
+ "002A9E9A" // CLA, INS, P1, P2
+ + dsi // digital signature input
+ + "00"; // Le
+
+ } else {
+ // Command APDU for INTERNAL AUTHENTICATE (page 55)
+ // This command doesn't take /
+ apdu =
+ "00880000" // CLA, INS, P1, P2
+ + dsi // digital signature input, card does PKCS#1
+ + "00"; // Le
+
+ }
String response = communicate(apdu);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
index 4d07025e6..4e7c06b69 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
@@ -200,10 +200,16 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
break;
}
case SECURITY_TOKEN_SIGN: {
- long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ long tokenSignKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
mSecurityTokenHelper.getKeyFingerprint(KeyType.SIGN));
- if (tokenKeyId != mRequiredInput.getSubKeyId()) {
+ long tokenAuthKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
+ mSecurityTokenHelper.getKeyFingerprint(KeyType.AUTH));
+
+ long requiredKey = mRequiredInput.getSubKeyId();
+
+
+ if ((tokenSignKeyId != requiredKey) && (tokenAuthKeyId != requiredKey)) {
throw new IOException(getString(R.string.error_wrong_security_token));
}
@@ -212,7 +218,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i];
- byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo);
+ byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo, requiredKey == tokenAuthKeyId);
mInputParcel.addCryptoData(hash, signedHash);
}
break;