Skip to content

Commit

Permalink
Merge pull request #35 from nelenkov/fp-authenticator
Browse files Browse the repository at this point in the history
Added support for keys protected by Android keystore
  • Loading branch information
npesic authored Oct 2, 2017
2 parents 6121608 + 4ec6901 commit cabc4be
Show file tree
Hide file tree
Showing 47 changed files with 1,742 additions and 473 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ gradle
.gradle
build
.classes
.idea
.idea/*
*.iml
local.properties
14 changes: 7 additions & 7 deletions fidouafclient/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 21
buildToolsVersion "22.0.1"
compileSdkVersion 25
buildToolsVersion "26.0.0"
useLibrary 'org.apache.http.legacy'

defaultConfig {
applicationId "org.ebayopensource.fidouafclient"
minSdkVersion 21
targetSdkVersion 21
targetSdkVersion 25
}

buildTypes {
Expand All @@ -19,9 +20,8 @@ android {
}

dependencies {
compile 'com.android.support:support-v4:18.0.0'
compile 'com.android.support:support-v4:25.0.0'
compile 'com.google.code.gson:gson:2.3.1'
compile files('libs/commons-codec-1.10.jar')
compile files('libs/sc-light-jdk15on-1.47.0.2.jar')
compile files('libs/scprov-jdk15on-1.47.0.3.jar')
compile group: 'com.madgag.spongycastle', name: 'prov', version: '1.58.0.0'
compile group: 'commons-codec', name: 'commons-codec', version: '1.10'
}
Binary file removed fidouafclient/app/libs/commons-codec-1.10.jar
Binary file not shown.
Binary file removed fidouafclient/app/libs/sc-light-jdk15on-1.47.0.2.jar
Binary file not shown.
Binary file removed fidouafclient/app/libs/scprov-jdk15on-1.47.0.3.jar
Binary file not shown.
5 changes: 3 additions & 2 deletions fidouafclient/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="21" />
android:minSdkVersion="23"
android:targetSdkVersion="23" />

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="org.fidoalliance.uaf.permissions.FIDO_CLIENT"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

<application
android:name="org.ebayopensource.fidouafclient.util.ApplicationContextProvider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,39 @@

package org.ebayopensource.fido.uaf.client;

import org.ebayopensource.fido.uaf.crypto.Base64url;
import org.ebayopensource.fidouafclient.util.Preferences;
import org.ebayopensource.fido.uaf.crypto.Asn1;
import android.os.Build;
import android.util.Log;

import org.ebayopensource.fido.uaf.crypto.BCrypt;
import org.ebayopensource.fido.uaf.crypto.KeyCodec;
import org.ebayopensource.fido.uaf.crypto.NamedCurve;
import org.ebayopensource.fido.uaf.crypto.Base64url;
import org.ebayopensource.fido.uaf.crypto.FidoSigner;
import org.ebayopensource.fido.uaf.crypto.SHA;
import org.ebayopensource.fido.uaf.msg.AuthenticationResponse;
import org.ebayopensource.fido.uaf.tlv.AlgAndEncodingEnum;
import org.ebayopensource.fido.uaf.tlv.TagsEnum;
import org.spongycastle.jce.interfaces.ECPublicKey;
import org.ebayopensource.fidouafclient.util.Preferences;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.logging.Logger;

public class AuthAssertionBuilder {

private Logger logger = Logger.getLogger(this.getClass().getName());
private static final String TAG = AuthAssertionBuilder.class.getSimpleName();

private static final Logger logger = Logger.getLogger(AuthAssertionBuilder.class.getName());

private FidoSigner fidoSigner;
private KeyPair signingKeyPair;

public AuthAssertionBuilder(FidoSigner fidoSigner, KeyPair signingKeyPair) {
this.fidoSigner = fidoSigner;
this.signingKeyPair = signingKeyPair;
}

public String getAssertions(AuthenticationResponse response) throws Exception {
ByteArrayOutputStream byteout = new ByteArrayOutputStream();
Expand Down Expand Up @@ -77,6 +88,28 @@ private byte[] getAuthAssertion(AuthenticationResponse response) throws Exceptio
return byteout.toByteArray();
}

private static byte[] makeAssertionInfo() {
//2 bytes - vendor; 1 byte Authentication Mode; 2 bytes Sig Alg
// XXX -- ugly. make this smarter and use consts
ByteBuffer bb = ByteBuffer.allocate(5);
bb.order(ByteOrder.LITTLE_ENDIAN);
//2 bytes - vendor
bb.put((byte)0x0);
bb.put((byte)0x0);
// 1 byte Authentication Mode;
bb.put((byte)0x1);
// 2 bytes Sig Alg
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
bb.putShort((short)AlgAndEncodingEnum.UAF_ALG_SIGN_SECP256R1_ECDSA_SHA256_DER.id);
//value = new byte[] { 0x00, 0x00, 0x01, 0x02, 0x00 };
} else {
//value = new byte[] { 0x00, 0x00, 0x01, 0x01, 0x00 };
bb.putShort((short)AlgAndEncodingEnum.UAF_ALG_SIGN_SECP256R1_ECDSA_SHA256_RAW.id);
}

return bb.array().clone();
}

private byte[] getSignedData(AuthenticationResponse response) throws IOException, NoSuchAlgorithmException {
ByteArrayOutputStream byteout = new ByteArrayOutputStream();
byte[] value = null;
Expand All @@ -89,8 +122,8 @@ private byte[] getSignedData(AuthenticationResponse response) throws IOException
byteout.write(value);

byteout.write(encodeInt(TagsEnum.TAG_ASSERTION_INFO.id));
//2 bytes - vendor; 1 byte Authentication Mode; 2 bytes Sig Alg
value = new byte[] { 0x00, 0x00, 0x01, 0x01, 0x00 };
value = makeAssertionInfo();

length = value.length;
byteout.write(encodeInt(length));
byteout.write(value);
Expand Down Expand Up @@ -138,32 +171,12 @@ private byte[] getCounters() throws IOException {
}

private byte[] getSignature(byte[] dataForSigning) throws Exception {
Log.d(TAG, "getSignature");

// PublicKey pub = KeyCodec.getPubKey(Base64
// .decodeBase64(TestData.TEST_PUB_KEY));

PublicKey pub =
KeyCodec.getPubKey(Base64url.decode(Preferences.getSettingsParam("pub")));
PrivateKey priv =
KeyCodec.getPrivKey(Base64url.decode(Preferences.getSettingsParam("priv")));
// KeyCodec.getPrivKey(Base64
// .decodeBase64(TestData.TEST_PRIV_KEY));

logger.info(" : dataForSigning : "
+ Base64url.encode(dataForSigning));

BigInteger[] signatureGen = NamedCurve.signAndFromatToRS(priv,
SHA.sha(dataForSigning, "SHA-256"));

boolean verify = NamedCurve.verify(
KeyCodec.getKeyAsRawBytes((ECPublicKey)pub),
SHA.sha(dataForSigning, "SHA-256"),
Asn1.decodeToBigIntegerArray(Asn1.getEncoded(signatureGen)));
if (!verify) {
throw new RuntimeException("Signatire match fail");
}
byte[] ret = Asn1.toRawSignatureBytes(signatureGen);
logger.info(" : signature : " + Base64url.encode(ret));
Log.i(TAG, "dataForSigning : " + Base64url.encode(dataForSigning));
byte[] ret = fidoSigner.sign(dataForSigning, signingKeyPair);

Log.i(TAG, " : signature : " + Base64url.encode(ret));

return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,28 @@
import com.google.gson.Gson;

import org.ebayopensource.fido.uaf.crypto.Base64url;
import org.ebayopensource.fido.uaf.crypto.FidoSigner;
import org.ebayopensource.fido.uaf.msg.AuthenticationRequest;
import org.ebayopensource.fido.uaf.msg.AuthenticationResponse;
import org.ebayopensource.fido.uaf.msg.AuthenticatorSignAssertion;
import org.ebayopensource.fido.uaf.msg.FinalChallengeParams;
import org.ebayopensource.fido.uaf.msg.OperationHeader;

import java.security.KeyPair;

public class AuthenticationRequestProcessor {

private FidoSigner fidoSigner;
private KeyPair signingKeyPair;

public AuthenticationRequestProcessor(FidoSigner fidoSigner, KeyPair signingKeyPair) {
this.fidoSigner = fidoSigner;
this.signingKeyPair = signingKeyPair;
}

public AuthenticationResponse processRequest(AuthenticationRequest request) {
AuthenticationResponse response = new AuthenticationResponse();
AuthAssertionBuilder builder = new AuthAssertionBuilder();
AuthAssertionBuilder builder = new AuthAssertionBuilder(fidoSigner, signingKeyPair);
Gson gson = new Gson();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,43 @@

package org.ebayopensource.fido.uaf.client;

import org.ebayopensource.fido.uaf.crypto.Base64url;
import org.ebayopensource.fidouafclient.util.Preferences;
import org.ebayopensource.fido.uaf.crypto.Asn1;
import android.util.Log;

import org.ebayopensource.fido.uaf.crypto.BCrypt;
import org.ebayopensource.fido.uaf.crypto.Base64url;
import org.ebayopensource.fido.uaf.crypto.FidoAttestationSigner;
import org.ebayopensource.fido.uaf.crypto.FixedCertFidoAttestationSigner;
import org.ebayopensource.fido.uaf.crypto.KeyCodec;
import org.ebayopensource.fido.uaf.crypto.NamedCurve;
import org.ebayopensource.fido.uaf.crypto.SHA;
import org.ebayopensource.fido.uaf.msg.RegistrationResponse;
import org.ebayopensource.fido.uaf.tlv.AlgAndEncodingEnum;
import org.ebayopensource.fido.uaf.tlv.Tags;
import org.ebayopensource.fido.uaf.tlv.TagsEnum;
import org.ebayopensource.fido.uaf.tlv.TlvAssertionParser;
import org.spongycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.spongycastle.jce.interfaces.ECPublicKey;
import org.ebayopensource.fidouafclient.util.Preferences;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.PublicKey;
import java.util.logging.Logger;


public class RegAssertionBuilder {

private static final String TAG = RegAssertionBuilder.class.getSimpleName();

public static final String AAID = "EBA0#0001";
private Logger logger = Logger.getLogger(this.getClass().getName());
private KeyPair keyPair = null;
private TlvAssertionParser parser = new TlvAssertionParser();

public RegAssertionBuilder (KeyPair keyPair){
this.keyPair = keyPair;

public RegAssertionBuilder (KeyPair keyPair) {
this.keyPair = keyPair;
}

public String getAssertions(RegistrationResponse response) throws Exception {
Expand All @@ -67,10 +69,13 @@ public String getAssertions(RegistrationResponse response) throws Exception {
String ret = Base64url.encodeToString(byteout.toByteArray());
logger.info(" : assertion : " + ret);
Tags tags = parser.parse(ret);
Log.d(TAG, "tags: " + tags.toString());
String AAID = new String(tags.getTags().get(
TagsEnum.TAG_AAID.id).value);
Log.d(TAG, "AAID: " + AAID);
String KeyID = new String(tags.getTags()
.get(TagsEnum.TAG_KEYID.id).value);
Log.d(TAG, "keyID: " + KeyID);
return ret;
}

Expand Down Expand Up @@ -114,7 +119,7 @@ private byte[] getAttestationBasicFull (byte[] signedDataValue) throws Exception
return byteout.toByteArray();
}

private byte[] getSignedData(RegistrationResponse response) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException {
private byte[] getSignedData(RegistrationResponse response) throws IOException, GeneralSecurityException {
ByteArrayOutputStream byteout = new ByteArrayOutputStream();
byte[] value = null;
int length = 0;
Expand All @@ -126,8 +131,9 @@ private byte[] getSignedData(RegistrationResponse response) throws IOException,
byteout.write(value);

byteout.write(encodeInt(TagsEnum.TAG_ASSERTION_INFO.id));
//2 bytes - vendor; 1 byte Authentication Mode; 2 bytes Sig Alg; 2 bytes Pub Key Alg
value = new byte[] { 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01 };
value = makeAssertionInfo();


length = value.length;
byteout.write(encodeInt(length));
byteout.write(value);
Expand Down Expand Up @@ -159,42 +165,40 @@ private byte[] getSignedData(RegistrationResponse response) throws IOException,
return byteout.toByteArray();
}

private byte[] makeAssertionInfo() {
//2 bytes - vendor; 1 byte Authentication Mode; 2 bytes Sig Alg; 2 bytes Pub Key Alg
ByteBuffer bb = ByteBuffer.allocate(7);
bb.order(ByteOrder.LITTLE_ENDIAN);
// 2 bytes - vendor assigned version
bb.put((byte)0x0);
bb.put((byte)0x0);
// 1 byte Authentication Mode;
bb.put((byte)0x1);
// 2 bytes Sig Alg
bb.putShort((short) AlgAndEncodingEnum.UAF_ALG_SIGN_SECP256R1_ECDSA_SHA256_RAW.id);
// 2 bytes Pub Key Alg
bb.putShort((short) AlgAndEncodingEnum.UAF_ALG_KEY_ECC_X962_RAW.id);

return bb.array().clone();
}

private byte[] getFC(RegistrationResponse response) throws NoSuchAlgorithmException {
return SHA.sha(response.fcParams.getBytes(), "SHA-256");
}

private byte[] getPubKeyId() throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
return KeyCodec.getKeyAsRawBytes((BCECPublicKey)this.keyPair.getPublic());
private byte[] getPubKeyId() throws GeneralSecurityException, IOException {
PublicKey pubKey = keyPair.getPublic();
Log.d(TAG, String.format("key: alg: %s enc: %s", pubKey.getAlgorithm(), pubKey.getFormat()));

return KeyCodec.getPubKeyAsRawBytes(pubKey);
}

private byte[] getSignature(byte[] dataForSigning) throws Exception {
FidoAttestationSigner attestSigner = new FixedCertFidoAttestationSigner();

// PublicKey pub = KeyCodec.getPubKey(
// Base64.encode(this.keyPair.getPublic().getEncoded(), Base64.URL_SAFE))
// ;
// PrivateKey priv = KeyCodec.getPrivKey(Base64
// .encode(this.keyPair.getPrivate().getEncoded(),Base64.URL_SAFE));
// PublicKey pub = this.keyPair.getPublic();
PrivateKey priv =
KeyCodec.getPrivKey(Base64url.decode(AttestCert.priv));
//this.keyPair.getPrivate();

logger.info(" : dataForSigning : "
+ Base64url.encodeToString(dataForSigning));

BigInteger[] signatureGen = NamedCurve.signAndFromatToRS(priv,
SHA.sha(dataForSigning, "SHA-256"));

boolean verify = NamedCurve.verify(
KeyCodec.getKeyAsRawBytes((ECPublicKey)KeyCodec.getPubKey(Base64url.decode(AttestCert.pubCert))),
//KeyCodec.getKeyAsRawBytes((ECPublicKey)this.keyPair.getPublic()),
SHA.sha(dataForSigning, "SHA-256"),
Asn1.decodeToBigIntegerArray(Asn1.getEncoded(signatureGen)));
if (!verify) {
throw new RuntimeException("Signatire match fail");
}
byte[] ret = Asn1.toRawSignatureBytes(signatureGen);
logger.info(" : signature : " + Base64url.encodeToString(ret));
Log.d(TAG, "dataForSigning : " + Base64url.encodeToString(dataForSigning));
byte[] ret = attestSigner.signWithAttestationCert(dataForSigning);
Log.d(TAG, "signature: " + Base64url.encodeToString(ret));

return ret;
}
Expand Down
Loading

0 comments on commit cabc4be

Please sign in to comment.