Commit 1612c7e3 authored by Josh Ji's avatar Josh Ji

code review

rename variables
parent f2fb5073
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="56" name="Java" />
</Languages>
</inspection_tool>
<inspection_tool class="GroovyAssignabilityCheck" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>
\ No newline at end of file
......@@ -69,6 +69,5 @@ javacard{
name "getCertificate"
scripts "select applet", "getCertificate"
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -19,6 +19,7 @@ package com.josh.vku2f;
import javacard.framework.JCSystem;
import javacard.framework.UserException;
import javacard.framework.Util;
import static com.josh.vku2f.CTAP2ErrorCode.*;
public class AuthenticatorGetAssertion {
public byte[] rpId;
......@@ -73,7 +74,7 @@ public class AuthenticatorGetAssertion {
// Read the map. It has 2 things in it.
vars[3] = decoder.readMajorType(CBORBase.TYPE_MAP);
if(vars[3] != 2) {
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
break;
}
for(vars[5] = 0; vars[5] < (short) 2; vars[5]++) {
......@@ -88,7 +89,7 @@ public class AuthenticatorGetAssertion {
// It doesn't matter what it is, just check it's string and exists.
} else {
// If it's not these two, throw an error
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
break;
}
}
......@@ -131,7 +132,7 @@ public class AuthenticatorGetAssertion {
}
// We should check we have our "mandatory" options
if(rpId == null || clientDataHash == null) {
UserException.throwIt(CTAP2.CTAP2_ERR_MISSING_PARAMETER);
UserException.throwIt(CTAP2_ERR_MISSING_PARAMETER);
}
// Good to go I guess
......
......@@ -19,6 +19,7 @@ package com.josh.vku2f;
import javacard.framework.JCSystem;
import javacard.framework.UserException;
import javacard.framework.Util;
import static com.josh.vku2f.CTAP2ErrorCode.*;
public class AuthenticatorMakeCredential {
public byte[] dataHash;
......@@ -33,7 +34,7 @@ public class AuthenticatorMakeCredential {
* Parses a CBOR structure to create an AuthenticatorMakeCredential object
*
* @param decoder the initialised decoder on the CBOR structure
* @param vars a short array to store variables in
* @ param vars a short array to store variables in
*/
public AuthenticatorMakeCredential(CBORDecoder decoder) throws UserException {
short[] vars;
......@@ -81,7 +82,7 @@ public class AuthenticatorMakeCredential {
len2 = decoder.readMajorType(CBORBase.TYPE_MAP);
// If less than 2, error
if (len2 < (short) 2) {
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
}
// Read the map iteratively
for (short j = 0; j < len2; j++) {
......@@ -163,7 +164,7 @@ public class AuthenticatorMakeCredential {
// Read the map length - should be 2
short len3 = decoder.readMajorType(CBORBase.TYPE_MAP);
if(len3 != 2) {
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
}
// Iterate over the map
for (short k = 0; k < (short) 2; k++) {
......@@ -197,10 +198,10 @@ public class AuthenticatorMakeCredential {
// Check it
decoder.readTextString(scratch1, (short) 0);
if(Util.arrayCompare(scratch1, (short) 0, Utf8Strings.UTF8_PUBLIC_KEY, (short) 0, (short) 10) != (byte) 0) {
UserException.throwIt(CTAP2.CTAP2_ERR_UNSUPPORTED_ALGORITHM);
UserException.throwIt(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
} else {
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
}
}
// Done
......@@ -216,7 +217,7 @@ public class AuthenticatorMakeCredential {
// Read the map. It has 2 things in it.
short len3 = decoder.readMajorType(CBORBase.TYPE_MAP);
if (len3 != 2) {
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_CBOR);
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
}
// Parse it, properly
for(short k = 0; k < (short) 2; k++) {
......@@ -231,7 +232,7 @@ public class AuthenticatorMakeCredential {
// It doesn't matter what it is, just check it's string and exists.
} else {
// If it's not these two, throw an error
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
break;
}
}
......@@ -243,7 +244,7 @@ public class AuthenticatorMakeCredential {
// Parse the two rk and uv objects
// Read the map
if(decoder.getMajorType() != CBORBase.TYPE_MAP) {
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
break;
}
len2 = decoder.readMajorType(CBORBase.TYPE_MAP);
......@@ -261,7 +262,7 @@ public class AuthenticatorMakeCredential {
decoder.readBoolean();
} else if (Util.arrayCompare(scratch1, (short) 0, Utf8Strings.UTF8_UP, (short) 0, (short) 2) == (short) 0) {
// Error out
UserException.throwIt(CTAP2.CTAP2_ERR_INVALID_OPTION);
UserException.throwIt(CTAP2_ERR_INVALID_OPTION);
break;
} else {
// Skip it
......@@ -275,7 +276,7 @@ public class AuthenticatorMakeCredential {
// We don't support any yet
// So check it's a map and skip
if(decoder.getMajorType() != CBORBase.TYPE_MAP) {
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
break;
}
decoder.skipEntry();
......@@ -291,7 +292,7 @@ public class AuthenticatorMakeCredential {
}
// Check we've got stuff like the clientDataHash
if(dataHash == null || rp == null || user == null || params == null) {
UserException.throwIt(CTAP2.CTAP2_ERR_MISSING_PARAMETER);
UserException.throwIt(CTAP2_ERR_MISSING_PARAMETER);
}
// We're done, I guess
......
......@@ -22,7 +22,7 @@ import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.UserException;
import javacard.framework.Util;
import static com.josh.vku2f.CTAP2ErrorCode.*;
public class CBORDecoder extends CBORBase {
/**
......@@ -101,7 +101,7 @@ public class CBORDecoder extends CBORBase {
*/
public short readMajorType(byte majorType) throws UserException {
if (majorType != getMajorType()) {
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
return 0;
}
return readLength();
......@@ -210,7 +210,7 @@ public class CBORDecoder extends CBORBase {
*/
public short readByteString(byte[] outBuffer, short outOffset) throws UserException {
if(getMajorType() != TYPE_BYTE_STRING) {
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
return 0;
}
short length = readLength();
......@@ -228,7 +228,7 @@ public class CBORDecoder extends CBORBase {
*/
public short readTextString(byte[] outBuffer, short outOffset) throws UserException {
if(getMajorType() != TYPE_TEXT_STRING) {
UserException.throwIt(CTAP2.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
UserException.throwIt(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
return 0;
}
short length = readLength();
......
......@@ -32,102 +32,63 @@ import javacard.security.KeyPair;
import javacard.security.MessageDigest;
import javacard.security.Signature;
import javacardx.apdu.ExtendedLength;
import static com.josh.vku2f.CTAP2ErrorCode.*;
public class CTAP2 extends Applet implements ExtendedLength {
private static final byte FIDO2_VENDOR_GET_COUNT = (byte)0x45;
private CBORDecoder cborDecoder;
private CBOREncoder cborEncoder;
private final CBORDecoder cborDecoder;
private final CBOREncoder cborEncoder;
private byte[] inBuf;
private byte[] inputBuffer;
private byte[] scratch;
private short[] vars;
private CredentialArray discoverableCreds;
private MessageDigest sha;
private AttestationKeyPair attestationKeyPair;
private byte[] info;
private StoredCredential[] assertionCreds;
private short[] nextAssertion;
AuthenticatorGetAssertion assertion;
private boolean persoComplete;
private boolean[] isChaining;
private short[] chainRam;
private short[] outChainRam;
private boolean[] isOutChaining;
private AuthenticatorMakeCredential cred;
private KeyPair ecDhKey;
private boolean[] ecDhSet;
private StoredCredential tempCred;
private static final byte ISO_INS_GET_DATA = (byte) 0xC0;
private static final byte FIDO2_INS_NFCCTAP_MSG = (byte) 0x10;
public static final byte CTAP1_ERR_SUCCESS = (byte) 0x00;
public static final byte CTAP1_ERR_INVALID_COMMAND = (byte) 0x01;
public static final byte CTAP1_ERR_INVALID_PARAMETER = (byte) 0x02;
public static final byte CTAP1_ERR_INVALID_LENGTH = (byte) 0x03;
public static final byte CTAP1_ERR_INVALID_SEQ = (byte) 0x04;
public static final byte CTAP1_ERR_TIMEOUT = (byte) 0x05;
public static final byte CTAP1_ERR_CHANNEL_BUSY = (byte) 0x06;
public static final byte CTAP1_ERR_LOCK_REQUIRED = (byte) 0x0A;
public static final byte CTAP1_ERR_INVALID_CHANNEL = (byte) 0x0B;
public static final byte CTAP1_ERR_OTHER = (byte) 0x7F;
public static final byte CTAP2_ERR_CBOR_UNEXPECTED_TYPE = (byte) 0x11;
public static final byte CTAP2_ERR_INVALID_CBOR = (byte) 0x12;
public static final byte CTAP2_ERR_MISSING_PARAMETER = (byte) 0x14;
public static final byte CTAP2_ERR_LIMIT_EXCEEDED = (byte) 0x15;
public static final byte CTAP2_ERR_UNSUPPORTED_EXTENSION = (byte) 0x16;
public static final byte CTAP2_ERR_CREDENTIAL_EXCLUDED = (byte) 0x19;
public static final byte CTAP2_ERR_PROCESSING = (byte) 0x21;
public static final byte CTAP2_ERR_INVALID_CREDENTIAL = (byte) 0x22;
public static final byte CTAP2_ERR_USER_ACTION_PENDING = (byte) 0x23;
public static final byte CTAP2_ERR_OPERATION_PENDING = (byte) 0x24;
public static final byte CTAP2_ERR_NO_OPERATIONS = (byte) 0x25;
public static final byte CTAP2_ERR_UNSUPPORTED_ALGORITHM = (byte) 0x26;
public static final byte CTAP2_ERR_OPERATION_DENIED = (byte) 0x27;
public static final byte CTAP2_ERR_KEY_STORE_FULL = (byte) 0x28;
public static final byte CTAP2_ERR_NO_OPERATION_PENDING = (byte) 0x2A;
public static final byte CTAP2_ERR_UNSUPPORTED_OPTION = (byte) 0x2B;
public static final byte CTAP2_ERR_INVALID_OPTION = (byte) 0x2C;
public static final byte CTAP2_ERR_KEEPALIVE_CANCEL = (byte) 0x2D;
public static final byte CTAP2_ERR_NO_CREDENTIALS = (byte) 0x2E;
public static final byte CTAP2_ERR_USER_ACTION_TIMEOUT = (byte) 0x2F;
public static final byte CTAP2_ERR_NOT_ALLOWED = (byte) 0x30;
public static final byte CTAP2_ERR_PIN_INVALID = (byte) 0x31;
public static final byte CTAP2_ERR_PIN_BLOCKED = (byte) 0x32;
public static final byte CTAP2_ERR_PIN_AUTH_INVALID = (byte) 0x33;
public static final byte CTAP2_ERR_PIN_AUTH_BLOCKED = (byte) 0x34;
public static final byte CTAP2_ERR_PIN_NOT_SET = (byte) 0x35;
public static final byte CTAP2_ERR_PIN_REQUIRED = (byte) 0x36;
public static final byte CTAP2_ERR_PIN_POLICY_VIOLATION = (byte) 0x37;
public static final byte CTAP2_ERR_PIN_TOKEN_EXPIRED = (byte) 0x38;
public static final byte CTAP2_ERR_REQUEST_TOO_LARGE = (byte) 0x39;
public static final byte CTAP2_ERR_ACTION_TIMEOUT = (byte) 0x3A;
public static final byte CTAP2_ERR_UP_REQUIRED = (byte) 0x3B;
public static final byte FIDO2_AUTHENTICATOR_MAKE_CREDENTIAL = (byte) 0x01;
public static final byte FIDO2_AUTHENTICATOR_GET_ASSERTION = (byte) 0x02;
public static final byte FIDO2_AUTHENTICATOR_GET_NEXT_ASSERTION = (byte) 0x08;
public static final byte FIDO2_AUTHENTICATOR_GET_INFO = (byte) 0x04;
public static final byte FIDO2_AUTHENTICATOR_CLIENT_PIN = (byte) 0x06;
public static final byte FIDO2_AUTHENTICATOR_RESET = (byte) 0x07;
private final short[] tempVars;
private final short[] chainRam;
private final short[] outChainRam;
private final short[] nextAssertion;
private final MessageDigest sha256MessageDigest;
private final AttestationKeyPair attestationKeyPair;
private CredentialArray credentialArray;
private AuthenticatorGetAssertion authenticatorGetAssertion;
private AuthenticatorMakeCredential authenticatorMakeCredential;
private final KeyPair ecDhKeyPair;
private final boolean[] ecDhSet;
private final boolean[] isChaining;
private final boolean[] isOutChaining;
private boolean personalizeComplete;
private StoredCredential tempCredential;
private StoredCredential[] assertionCredentials;
public static final byte ISO_INS_GET_DATA = (byte) 0xC0;
public static final byte FIDO2_INS_NFCCTAP_MSG = (byte) 0x10;
public static final byte FIDO2_AUTHENTICATOR_MAKE_CREDENTIAL = (byte) 0x01;
public static final byte FIDO2_AUTHENTICATOR_GET_ASSERTION = (byte) 0x02;
public static final byte FIDO2_AUTHENTICATOR_GET_NEXT_ASSERTION = (byte) 0x08;
public static final byte FIDO2_AUTHENTICATOR_GET_INFO = (byte) 0x04;
public static final byte FIDO2_AUTHENTICATOR_CLIENT_PIN = (byte) 0x06;
public static final byte FIDO2_AUTHENTICATOR_RESET = (byte) 0x07;
// Vendor specific - for attestation cert loading.
public static final byte FIDO2_VENDOR_ATTEST_SIGN = (byte) 0x41;
public static final byte FIDO2_VENDOR_ATTEST_LOADCERT = (byte) 0x42;
public static final byte FIDO2_VENDOR_PERSO_COMPLETE = (byte) 0x43;
public static final byte FIDO2_VENDOR_ATTEST_GETPUB = (byte) 0x44;
public static final byte FIDO2_VENDOR_ATTEST_GETCERT = (byte) 0x4A;
public static final byte FIDO2_VENDOR_ATTEST_SIGN = (byte) 0x41;
public static final byte FIDO2_VENDOR_ATTEST_LOADCERT = (byte) 0x42;
public static final byte FIDO2_VENDOR_PERSO_COMPLETE = (byte) 0x43;
public static final byte FIDO2_VENDOR_ATTEST_GETPUB = (byte) 0x44;
public static final byte FIDO2_VENDOR_GET_COUNT = (byte) 0x45;
public static final byte FIDO2_VENDOR_ATTEST_GETCERT = (byte) 0x4A;
public static final byte FIDO2_DESELECT = 0x12;
// AAGUID - this uniquely identifies the type of authenticator we have built.
// AAGUID - Authenticator Attestation Global Unique Identifier
// this uniquely identifies the type of authenticator we have built.
// If you're reusing this code, please generate your own GUID and put it here -
// this is unique to manufacturer and device model.
public static final byte[] aaguid = {
(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) 'g', (byte) 'u', (byte) 'i', (byte) 'd',
(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
private CTAP2() {
......@@ -135,24 +96,24 @@ public class CTAP2 extends Applet implements ExtendedLength {
// 1210 bytes of a transient buffer for read-in and out
// We advertise 1200 bytes supported, but 10 bytes for protocol nonsense
try {
inBuf = JCSystem.makeTransientByteArray((short) 1210, JCSystem.CLEAR_ON_DESELECT);
inputBuffer = JCSystem.makeTransientByteArray((short) 1210, JCSystem.CLEAR_ON_DESELECT);
} catch (Exception e) {
inBuf = new byte[1210];
inputBuffer = new byte[1210];
}
try {
scratch = JCSystem.makeTransientByteArray((short) 512, JCSystem.CLEAR_ON_DESELECT);
} catch (Exception e) {
scratch = new byte[512];
}
vars = JCSystem.makeTransientShortArray((short) 8, JCSystem.CLEAR_ON_DESELECT);
tempVars = JCSystem.makeTransientShortArray((short) 8, JCSystem.CLEAR_ON_DESELECT);
// Create the CBOR decoder
cborDecoder = new CBORDecoder();
cborEncoder = new CBOREncoder();
discoverableCreds = new CredentialArray((short) 5);
sha = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
credentialArray = new CredentialArray((short) 5);
sha256MessageDigest = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
attestationKeyPair = new AttestationKeyPair();
nextAssertion = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
persoComplete = false;
personalizeComplete = false;
isChaining = JCSystem.makeTransientBooleanArray((short) 2, JCSystem.CLEAR_ON_DESELECT);
chainRam = JCSystem.makeTransientShortArray((short) 4, JCSystem.CLEAR_ON_DESELECT);
outChainRam = JCSystem.makeTransientShortArray((short) 4, JCSystem.CLEAR_ON_DESELECT);
......@@ -161,29 +122,29 @@ public class CTAP2 extends Applet implements ExtendedLength {
JCSystem.MEMORY_TYPE_TRANSIENT_RESET, KeyBuilder.LENGTH_EC_FP_256, false);
ECPrivateKey ecDhPriv = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PRIVATE,
JCSystem.MEMORY_TYPE_TRANSIENT_RESET, KeyBuilder.LENGTH_EC_FP_256, false);
ecDhKey = new KeyPair(ecDhPub, ecDhPriv);
ecDhKeyPair = new KeyPair(ecDhPub, ecDhPriv);
ecDhSet = JCSystem.makeTransientBooleanArray((short) 1, JCSystem.CLEAR_ON_RESET);
}
public void handle(APDU apdu) {
byte[] buffer = apdu.getBuffer();
tempCred = null;
cred = null;
vars[3] = doApduIngestion(apdu);
if (vars[3] == 0) {
tempCredential = null;
authenticatorMakeCredential = null;
tempVars[3] = doApduIngestion(apdu);
if (tempVars[3] == 0) {
// If zero, we had no ISO error, but there might be a CTAP error to return.
// Throw either way.
ISOException.throwIt(ISO7816.SW_NO_ERROR);
return;
}
// Need to grab the CTAP command byte
switch (inBuf[0]) {
switch (inputBuffer[0]) {
case FIDO2_AUTHENTICATOR_MAKE_CREDENTIAL:
authMakeCredential(apdu, vars[3]);
authMakeCredential(apdu, tempVars[3]);
break;
case FIDO2_AUTHENTICATOR_GET_ASSERTION:
authGetAssertion(apdu, vars[3]);
authGetAssertion(apdu, tempVars[3]);
break;
case FIDO2_AUTHENTICATOR_GET_INFO:
authGetInfo(apdu);
......@@ -192,10 +153,13 @@ public class CTAP2 extends Applet implements ExtendedLength {
authGetNextAssertion(apdu, buffer);
break;
case FIDO2_VENDOR_ATTEST_SIGN: //0x41
attestSignRaw(apdu, vars[3]);
attestSignRaw(apdu, tempVars[3]);
break;
case FIDO2_AUTHENTICATOR_CLIENT_PIN:
clientPin(apdu, tempVars[3]);
break;
case FIDO2_VENDOR_ATTEST_LOADCERT: //0x42
attestSetCert(apdu, vars[3]);
attestSetCert(apdu, tempVars[3]);
break;
case FIDO2_VENDOR_PERSO_COMPLETE: //0x43
persoComplete(apdu);
......@@ -210,7 +174,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
getCount(apdu);
break;
case FIDO2_AUTHENTICATOR_RESET: //0x07
// Need to finish doing this, we can, i mean, but I don't like it
// Need to finish doing this, we can, I mean, but I don't like it
doReset(apdu);
break;
default:
......@@ -220,8 +184,8 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
public void persoComplete(APDU apdu) {
if (attestationKeyPair.isCertSet() && !persoComplete) {
persoComplete = true;
if (attestationKeyPair.isCertSet() && !personalizeComplete) {
personalizeComplete = true;
returnError(apdu, CTAP1_ERR_SUCCESS);
} else {
returnError(apdu, CTAP1_ERR_INVALID_COMMAND);
......@@ -231,113 +195,108 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Gets the attestation public key.
*
* @param apdu
* @ param buffer
* @ param inBuf
* @ param bufLen
* @param apdu apdu buffer
*/
public void getAttestPublic(APDU apdu) {
if (persoComplete) {
if (personalizeComplete) {
returnError(apdu, CTAP1_ERR_INVALID_COMMAND);
return;
}
inBuf[0] = 0x00;
vars[0] = (short) (attestationKeyPair.getPubkey(inBuf, (short) 1) + 1);
inputBuffer[0] = 0x00;
tempVars[0] = (short) (attestationKeyPair.getPubkey(inputBuffer, (short) 1) + 1);
apdu.setOutgoing();
apdu.setOutgoingLength(vars[0]);
apdu.sendBytesLong(inBuf, (short) 0, vars[0]);
apdu.setOutgoingLength(tempVars[0]);
apdu.sendBytesLong(inputBuffer, (short) 0, tempVars[0]);
}
/** get counter's value */
public void getCount(APDU apdu){
short count = discoverableCreds.getCount();
short count = credentialArray.getCount();
apdu.setOutgoing();
apdu.setOutgoingLength((short)2);
Util.setShort(inBuf,(short)0, count);
apdu.sendBytesLong(inBuf,(short)0,(short)2);
Util.setShort(inputBuffer,(short)0, count);
apdu.sendBytesLong(inputBuffer,(short)0,(short)2);
}
/**
* Performs raw signatures, may only occur when personalisation is not complete.
*
* @param apdu
* @ param buffer
* @ param inBuf
* @param bufLen
*
* @param apdu apdu buffer
* @param bufLen buffer length
*/
public void attestSignRaw(APDU apdu, short bufLen) {
if (persoComplete) {
if (personalizeComplete) {
returnError(apdu, CTAP1_ERR_INVALID_COMMAND);
return;
}
Util.arrayCopy(inBuf, (short) 1, scratch, (short) 0, (short) (bufLen - 1));
inBuf[0] = 0x00;
vars[2] = attestationKeyPair.sign(scratch, (short) 0, vars[1], inBuf, (short) 1);
Util.arrayCopy(inputBuffer, (short) 1, scratch, (short) 0, (short) (bufLen - 1));
inputBuffer[0] = 0x00;
tempVars[2] = attestationKeyPair.sign(scratch, (short) 0, tempVars[1], inputBuffer, (short) 1);
apdu.setOutgoing();
apdu.setOutgoingLength((short) (vars[2] + 1));
apdu.sendBytesLong(inBuf, (short) 0, (short) (vars[2] + 1));
apdu.setOutgoingLength((short) (tempVars[2] + 1));
apdu.sendBytesLong(inputBuffer, (short) 0, (short) (tempVars[2] + 1));
}
public void attestSetCert(APDU apdu, short bufLen) {
if (persoComplete) {
if (personalizeComplete) {
returnError(apdu, CTAP1_ERR_INVALID_COMMAND);
return;
}
// We don't actually use any CBOR here, simplify copying
attestationKeyPair.setCert(inBuf, (short) 1, (short) (bufLen - 1));
attestationKeyPair.setCert(inputBuffer, (short) 1, (short) (bufLen - 1));
MessageDigest dig = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
short len = (short) (dig.doFinal(attestationKeyPair.x509cert, (short) 0, attestationKeyPair.x509len, inBuf, (short) 3) + 3);
inBuf[0] = 0x00;
Util.setShort(inBuf, (short) 1, attestationKeyPair.x509len);
short len = (short) (dig.doFinal(attestationKeyPair.x509cert, (short) 0, attestationKeyPair.x509len, inputBuffer, (short) 3) + 3);
inputBuffer[0] = 0x00;
Util.setShort(inputBuffer, (short) 1, attestationKeyPair.x509len);
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytesLong(inBuf, (short) 0, len);
apdu.sendBytesLong(inputBuffer, (short) 0, len);
}
public void authMakeCredential(APDU apdu, short bufLen) {
// Init the decoder
cborDecoder.init(inBuf, (short) 1, bufLen);
cborDecoder.init(inputBuffer, (short) 1, bufLen);
// create a credential object
try {
cred = new AuthenticatorMakeCredential(cborDecoder);
authenticatorMakeCredential = new AuthenticatorMakeCredential(cborDecoder);
} catch (UserException e) {
returnError(apdu, e.getReason());
return;
}
// Create the actual credential
switch (cred.getAlgorithm()) {
switch (authenticatorMakeCredential.getAlgorithm()) {
case Signature.ALG_ECDSA_SHA_256:
tempCred = new StoredES256Credential(cred);
tempCredential = new StoredES256Credential(authenticatorMakeCredential);
break;
case Signature.ALG_RSA_SHA_256_PKCS1:
tempCred = new StoredRS256Credential(cred);
tempCredential = new StoredRS256Credential(authenticatorMakeCredential);
break;
case Signature.ALG_RSA_SHA_256_PKCS1_PSS:
tempCred = new StoredPS256Credential(cred);
tempCredential = new StoredPS256Credential(authenticatorMakeCredential);
break;
default:
returnError(apdu, CTAP2_ERR_UNSUPPORTED_ALGORITHM);
return;
}
if (cred.isResident()) {
// Check if a credential exists on the exclude list
if (authenticatorMakeCredential.isResident()) {
// Check if a credential exists on the excluded list
if (cred.isExclude() && isPresent(cred.exclude)) {
if (authenticatorMakeCredential.isExclude() && isPresent(authenticatorMakeCredential.exclude)) {
// Throw the error
returnError(apdu, CTAP2_ERR_CREDENTIAL_EXCLUDED);
return;
}
// Add the credential to the resident storage, overwriting if necessary
addResident(apdu, tempCred);
addResident(apdu, tempCredential);
// Initialise the output buffer, for CBOR writing.
// output buffer needs 0x00 as first byte as status code
inBuf[0] = 0x00;
cborEncoder.init(inBuf, (short) 1, (short) 1199);
inputBuffer[0] = 0x00;
cborEncoder.init(inputBuffer, (short) 1, (short) 1199);
// Create a map in the buffer
vars[0] = cborEncoder.startMap((short) 3);
tempVars[0] = cborEncoder.startMap((short) 3);
// Attestation stuff
cborEncoder.writeRawByte((byte) 0x01);
......@@ -346,19 +305,19 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Put the authdata identifier there
cborEncoder.writeRawByte((byte) 0x02);
// Allocate some space for the byte string
vars[0] = cborEncoder.startByteString((short) (37 + tempCred.getAttestedLen()));
tempVars[0] = cborEncoder.startByteString((short) (37 + tempCredential.getAttestedLen()));
// Stash where it begins
vars[7] = vars[0];
tempVars[7] = tempVars[0];
// Create the SHA256 hash of the RP ID
tempCred.rp.getRp(scratch, (short) 0);
vars[0] += sha.doFinal(scratch, (short) 0, tempCred.rp.getRpLen(), inBuf, vars[0]);
tempCredential.rpEntity.getRp(scratch, (short) 0);
tempVars[0] += sha256MessageDigest.doFinal(scratch, (short) 0, tempCredential.rpEntity.getRpLen(), inputBuffer, tempVars[0]);
// Set flags - User presence, user verified, attestation present
inBuf[vars[0]++] = (byte) 0x45;
inputBuffer[tempVars[0]++] = (byte) 0x45;
// Set the signature counter
vars[0] += tempCred.readCounter(inBuf, vars[0]);
tempVars[0] += tempCredential.readCounter(inputBuffer, tempVars[0]);
// Read the credential details in
// Just note down where this starts for future ref
vars[0] += tempCred.getAttestedData(inBuf, vars[0]);
tempVars[0] += tempCredential.getAttestedData(inputBuffer, tempVars[0]);
// Generate and then attach the attestation
cborEncoder.writeRawByte((byte) 0x03);
......@@ -378,11 +337,11 @@ public class CTAP2 extends Applet implements ExtendedLength {
// We sign over the client data hash and the attested data.
// AuthenticatorData is first. We noted down where it begins and know how long
// it is.
attestationKeyPair.update(inBuf, vars[7], (short) (tempCred.getAttestedLen() + 37));
attestationKeyPair.update(inputBuffer, tempVars[7], (short) (tempCredential.getAttestedLen() + 37));
// The client data hash is next, which we use to finish off the signature.
vars[4] = attestationKeyPair.sign(cred.dataHash, (short) 0, (short) cred.dataHash.length, scratch, (short) 0);
tempVars[4] = attestationKeyPair.sign(authenticatorMakeCredential.dataHash, (short) 0, (short) authenticatorMakeCredential.dataHash.length, scratch, (short) 0);
// Create the byte string for the signature
cborEncoder.encodeByteString(scratch, (short) 0, vars[4]);
cborEncoder.encodeByteString(scratch, (short) 0, tempVars[4]);
// Set the x509 cert now
cborEncoder.encodeTextString(Utf8Strings.UTF8_X5C, (short) 0, (short) 3);
// Supposedly we need an array here
......@@ -402,41 +361,41 @@ public class CTAP2 extends Applet implements ExtendedLength {
public void authGetAssertion(APDU apdu, short bufLen) {
nextAssertion[0] = (short) 0;
// Decode the CBOR array for the assertion
cborDecoder.init(inBuf, (short) 1, bufLen);
cborDecoder.init(inputBuffer, (short) 1, bufLen);
try {
assertion = new AuthenticatorGetAssertion(cborDecoder);
authenticatorGetAssertion = new AuthenticatorGetAssertion(cborDecoder);
} catch (UserException e) {
returnError(apdu, e.getReason());
return;
}
// Match the assertion to the credential
// Get a list of matching credentials
assertionCreds = findCredentials(apdu, assertion);
assertionCredentials = findCredentials(apdu, authenticatorGetAssertion);
// Use the first one; this complies with both ideas - use the most recent match
// if no allow list, use any if an allow list existed
if (assertionCreds.length == 0 || assertionCreds[0] == null) {
// if no allow list, use any if an allowing list existed
if (assertionCredentials.length == 0 || assertionCredentials[0] == null) {
returnError(apdu, CTAP2_ERR_NO_CREDENTIALS);
return;
}
// Create the authenticatorData to sign
sha.doFinal(assertion.rpId, (short) 0, (short) assertion.rpId.length, scratch, (short) 0);
if (assertion.options[1]) {
sha256MessageDigest.doFinal(authenticatorGetAssertion.rpId, (short) 0, (short) authenticatorGetAssertion.rpId.length, scratch, (short) 0);
if (authenticatorGetAssertion.options[1]) {
scratch[32] = 0x05;
} else {
scratch[32] = 0x01;
}
assertionCreds[0].readCounter(scratch, (short) 33);
assertionCredentials[0].readCounter(scratch, (short) 33);
// Copy the hash in
assertion.getHash(scratch, (short) 37);
authenticatorGetAssertion.getHash(scratch, (short) 37);
// Create the output
// Status flags first
inBuf[0] = 0x00;
inputBuffer[0] = 0x00;
// Create the encoder
cborEncoder.init(inBuf, (short) 1, (short) 1199);
cborEncoder.init(inputBuffer, (short) 1, (short) 1199);
// Determine if we need 4 or 5 in the array
if (assertionCreds.length > 1) {
if (assertionCredentials.length > 1) {
doAssertionCommon(cborEncoder, (short) 5);
} else {
doAssertionCommon(cborEncoder, (short) 4);
......@@ -449,30 +408,28 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Get the next assertion in a list of multiple.
*
* @param apdu
* @param buffer
* @ param inBuf
* @ param inLen
* @param apdu apdu buffer
* @param buffer buffer
*/
private void authGetNextAssertion(APDU apdu, byte[] buffer) {
// Confirm that we have more assertions to do
if (nextAssertion[0] != (short) 0 && nextAssertion[0] < assertionCreds.length) {
if (nextAssertion[0] != (short) 0 && nextAssertion[0] < assertionCredentials.length) {
// Create the authenticatorData to sign
sha.doFinal(assertion.rpId, (short) 0, (short) assertion.rpId.length, scratch, (short) 0);
if (assertion.options[1]) {
sha256MessageDigest.doFinal(authenticatorGetAssertion.rpId, (short) 0, (short) authenticatorGetAssertion.rpId.length, scratch, (short) 0);
if (authenticatorGetAssertion.options[1]) {
scratch[32] = 0x05;
} else {
scratch[32] = 0x01;
}
assertionCreds[nextAssertion[0]].readCounter(scratch, (short) 33);
assertionCredentials[nextAssertion[0]].readCounter(scratch, (short) 33);
// Copy the hash in
assertion.getHash(scratch, (short) 37);
authenticatorGetAssertion.getHash(scratch, (short) 37);
// Create the output
// Status flags first
inBuf[0] = 0x00;
inputBuffer[0] = 0x00;
// Create the encoder
cborEncoder.init(inBuf, (short) 1, (short) 1199);
cborEncoder.init(inputBuffer, (short) 1, (short) 1199);
doAssertionCommon(cborEncoder, (short) 4);
nextAssertion[0]++;
......@@ -485,15 +442,15 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Note: we only implement the keyAgreement bit
public void clientPin(APDU apdu, short bufLen) {
try {
cborDecoder.init(inBuf, (short) 1, bufLen);
cborDecoder.init(inputBuffer, (short) 1, bufLen);
// Start reading
cborDecoder.readMajorType(CBORBase.TYPE_MAP);
// Read PIN protocol tag
// Read pinUvAuthProtocol
if (cborDecoder.readInt8() != (byte) 0x01) {
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
return;
}
// Read the actual protocol
// Read subCommand
if (cborDecoder.readInt8() != (byte) 0x01) {
UserException.throwIt(CTAP2_ERR_INVALID_CBOR);
return;
......@@ -511,7 +468,6 @@ public class CTAP2 extends Applet implements ExtendedLength {
break;
default:
UserException.throwIt(CTAP2_ERR_UNSUPPORTED_OPTION);
return;
}
} catch (UserException e) {
returnError(apdu, e.getReason());
......@@ -521,14 +477,14 @@ public class CTAP2 extends Applet implements ExtendedLength {
private void addResident(APDU apdu, StoredCredential cred) {
// Add a Discoverable Credential (resident)
try {
discoverableCreds.addCredential(cred);
credentialArray.addCredential(cred);
} catch (UserException e) {
returnError(apdu, e.getReason());
}
}
// Generate a session-specific ECDH P-256 key for Diffie-Hellman with the
// platform (Used for PIN but we only ever do it for hmac-secret)
// platform (Used for PIN ,but we only ever do it for hmac-secret)
private void generateDH(APDU apdu) {
byte[] w;
try {
......@@ -540,16 +496,16 @@ public class CTAP2 extends Applet implements ExtendedLength {
if (!ecDhSet[0]) {
// Grab the public key and set it's parameters
KeyParams.sec256r1params((ECKey) ecDhKey.getPublic());
// Grab the public key and set its parameters
KeyParams.sec256r1params((ECKey) ecDhKeyPair.getPublic());
// Generate a new key-pair
ecDhKey.genKeyPair();
ecDhKeyPair.genKeyPair();
}
((ECPublicKey) ecDhKey.getPublic()).getW(w, (short) 0);
((ECPublicKey) ecDhKeyPair.getPublic()).getW(w, (short) 0);
// Return the data requested
inBuf[0] = 0x00;
cborEncoder.init(inBuf, (short) 1, (short) 1199);
inputBuffer[0] = 0x00;
cborEncoder.init(inputBuffer, (short) 1, (short) 1199);
// Start a map
cborEncoder.startMap((short) 1);
// Encode the COSE key identifier
......@@ -595,21 +551,21 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Our list can be no bigger than the allowList
list = new StoredCredential[(short) assertion.allow.length];
vars[6] = 0;
for (vars[7] = (short) (discoverableCreds.getLength() - 1); vars[7] >= 0; vars[7]--) {
temp = discoverableCreds.getCred(vars[7]);
tempVars[6] = 0;
for (tempVars[7] = (short) (credentialArray.getLength() - 1); tempVars[7] >= 0; tempVars[7]--) {
temp = credentialArray.get(tempVars[7]);
// Check if null or doesn't match rpId
if (temp != null && temp.rp.checkId(assertion.rpId, (short) 0, (short) assertion.rpId.length)) {
for (vars[5] = 0; vars[5] < (short) assertion.allow.length; vars[5]++) {
if (temp != null && temp.rpEntity.checkId(assertion.rpId, (short) 0, (short) assertion.rpId.length)) {
for (tempVars[5] = 0; tempVars[5] < (short) assertion.allow.length; tempVars[5]++) {
// Check the list
// Does length match?
if ((short) assertion.allow[vars[5]].id.length != (short) temp.id.length) {
if ((short) assertion.allow[tempVars[5]].id.length != (short) temp.credentialId.length) {
continue;
}
if (Util.arrayCompare(assertion.allow[vars[5]].id, (short) 0, temp.id, (short) 0,
(short) temp.id.length) == 0) {
if (Util.arrayCompare(assertion.allow[tempVars[5]].id, (short) 0, temp.credentialId, (short) 0,
(short) temp.credentialId.length) == 0) {
// Add it to the list
list[vars[6]++] = temp;
list[tempVars[6]++] = temp;
}
}
......@@ -619,23 +575,23 @@ public class CTAP2 extends Applet implements ExtendedLength {
} else {
// Old code path, works fine for me
list = new StoredCredential[discoverableCreds.getLength()];
vars[6] = 0;
for (vars[7] = (short) (discoverableCreds.getLength() - 1); vars[7] >= 0; vars[7]--) {
temp = discoverableCreds.getCred(vars[7]);
list = new StoredCredential[credentialArray.getLength()];
tempVars[6] = 0;
for (tempVars[7] = (short) (credentialArray.getLength() - 1); tempVars[7] >= 0; tempVars[7]--) {
temp = credentialArray.get(tempVars[7]);
// Check for null or doesn't match rpId
if (temp != null && temp.rp.checkId(assertion.rpId, (short) 0, (short) assertion.rpId.length)) {
if (temp != null && temp.rpEntity.checkId(assertion.rpId, (short) 0, (short) assertion.rpId.length)) {
// Then valid
list[vars[6]++] = temp;
list[tempVars[6]++] = temp;
}
}
}
// Trim the list
StoredCredential[] ret = new StoredCredential[vars[6]];
StoredCredential[] ret = new StoredCredential[tempVars[6]];
// Trim
for (vars[7] = 0; vars[7] < vars[6]; vars[7]++) {
ret[vars[7]] = list[vars[7]];
for (tempVars[7] = 0; tempVars[7] < tempVars[6]; tempVars[7]++) {
ret[tempVars[7]] = list[tempVars[7]];
}
// Null out the unused stuff
JCSystem.requestObjectDeletion();
......@@ -646,18 +602,18 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Check if anything in the list is present
*
* @param list
* @return
* @param list list
* @return if is present
*/
private boolean isPresent(PublicKeyCredentialDescriptor[] list) {
StoredCredential temp;
for (vars[7] = (short) 0; vars[7] < discoverableCreds.getLength(); vars[7]++) {
temp = discoverableCreds.getCred(vars[7]);
for (tempVars[7] = (short) 0; tempVars[7] < credentialArray.getLength(); tempVars[7]++) {
temp = credentialArray.get(tempVars[7]);
if (temp == null) {
continue;
}
for (vars[6] = (short) 0; vars[6] < (short) list.length; vars[6]++) {
if (temp.checkId(list[vars[6]].id, (short) 0, (short) list[vars[6]].id.length)) {
for (tempVars[6] = (short) 0; tempVars[6] < (short) list.length; tempVars[6]++) {
if (temp.checkId(list[tempVars[6]].id, (short) 0, (short) list[tempVars[6]].id.length)) {
return true;
}
}
......@@ -667,21 +623,21 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
/**
* Reset the authenticator. This doesn't actually take much. TODO: Implement
* Reset the authenticator. This doesn't actually take much.
* checking. This is just so testing doesn't crap out.
*/
private void doReset(APDU apdu) {
discoverableCreds = new CredentialArray((short) 5);
// TODO: Implement Resetting
credentialArray = new CredentialArray((short) 5);
JCSystem.requestObjectDeletion();
returnError(apdu, CTAP1_ERR_SUCCESS);
}
/**
* Return an error via APDU - an error on the FIDO2 side is considered a success
* in APDU-land so we send a response.
* in APDU-land ,so we send a response.
*
* @param apdu shared APDU object
* @ param buffer APDU buffer
* @param err error code
*/
public void returnError(APDU apdu, byte err) {
......@@ -692,10 +648,9 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Return an error via APDU - an error on the FIDO2 side is considered a success
* in APDU-land so we send a response.
* in APDU-land ,so we send a response.
*
* @param apdu shared APDU object
* @ param buffer APDU buffer
* @param err error code
*/
public void returnError(APDU apdu, short err) {
......@@ -706,19 +661,16 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
/**
* Get authenticator-specific informtion, and return it to the platform.
* Get authenticator-specific information, and return it to the platform.
*
* @param apdu
* @ param buffer
* @ param inBuf
* @ param bufLen
* @param apdu apdu buffer
*/
public void authGetInfo(APDU apdu) {
// Create the authenticator info if not present.
if (info == null) {
// Create the authGetInfo - 0x00 is success
inBuf[0] = 0x00;
cborEncoder.init(inBuf, (short) 1, (short) 1199);
inputBuffer[0] = 0x00;
cborEncoder.init(inputBuffer, (short) 1, (short) 1199);
cborEncoder.startMap((short) 4);
// 0x01, versions
cborEncoder.encodeUInt8((byte) 0x01);
......@@ -748,38 +700,38 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Done
JCSystem.beginTransaction();
info = new byte[cborEncoder.getCurrentOffset()];
Util.arrayCopy(inBuf, (short) 0, info, (short) 0, cborEncoder.getCurrentOffset());
Util.arrayCopy(inputBuffer, (short) 0, info, (short) 0, cborEncoder.getCurrentOffset());
JCSystem.commitTransaction();
}
// Send it
Util.arrayCopyNonAtomic(info, (short) 0, inBuf, (short) 0, (short) info.length);
Util.arrayCopyNonAtomic(info, (short) 0, inputBuffer, (short) 0, (short) info.length);
sendLongChaining(apdu, (short) info.length);
}
/**
* Covers the common assertion building process.
*
* @param enc
* @param mapLen
* @param encoder CBOR Encoder
* @param mapLength Map Length
*/
private void doAssertionCommon(CBOREncoder enc, short mapLen) {
private void doAssertionCommon(CBOREncoder encoder, short mapLength) {
// Determine if we need 4 or 5 in the array
if (mapLen == 4) {
enc.startMap((short) 4);
if (mapLength == 4) {
encoder.startMap((short) 4);
} else {
enc.startMap((short) 5);
encoder.startMap((short) 5);
}
// Tag 1, credential data
enc.encodeUInt8((byte) 0x01);
encoder.encodeUInt8((byte) 0x01);
// Start a map, which is all the PublicKeyCredentialDescriptor is
enc.startMap((short) 2);
encoder.startMap((short) 2);
// Put the id key
cborEncoder.encodeTextString(Utf8Strings.UTF8_ID, (short) 0, (short) 2);
// Put the value, which is a byte array
cborEncoder.encodeByteString(assertionCreds[nextAssertion[0]].id, (short) 0,
(short) assertionCreds[nextAssertion[0]].id.length);
cborEncoder.encodeByteString(assertionCredentials[nextAssertion[0]].credentialId, (short) 0,
(short) assertionCredentials[nextAssertion[0]].credentialId.length);
// Put the key for the type
cborEncoder.encodeTextString(Utf8Strings.UTF8_TYPE, (short) 0, (short) 4);
// Put the value
......@@ -795,57 +747,57 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Turns out this is DER encoding, again
// Sign the data
vars[3] = assertionCreds[nextAssertion[0]].performSignature(scratch, (short) 0, (short) 69, scratch,
tempVars[3] = assertionCredentials[nextAssertion[0]].performSignature(scratch, (short) 0, (short) 69, scratch,
(short) 69);
// Create the ByteString to put it into
cborEncoder.encodeByteString(scratch, (short) 69, vars[3]);
cborEncoder.encodeByteString(scratch, (short) 69, tempVars[3]);
// Tag 4, user details
cborEncoder.encodeUInt8((byte) 0x04);
// Start the PublicKeyCredentialUserEntity map
// If we have "UV" enabled, then we do all the info we have.
if (assertion.options[1]) {
cborEncoder.startMap(assertionCreds[nextAssertion[0]].user.numData);
if (authenticatorGetAssertion.options[1]) {
cborEncoder.startMap(assertionCredentials[nextAssertion[0]].userEntity.numData);
// We need to check what we have for users
// Iterate over the bit flags
boolean[] usrFlags = assertionCreds[nextAssertion[0]].getPresentUser();
boolean[] usrFlags = assertionCredentials[nextAssertion[0]].getPresentUser();
// This actually
if (usrFlags[2]) {
// Has the 'displayName' tag
cborEncoder.encodeTextString(Utf8Strings.UTF8_DISPLAYNAME, (short) 0, (short) 11);
cborEncoder.encodeTextString(assertionCreds[nextAssertion[0]].user.displayName.str, (short) 0,
assertionCreds[nextAssertion[0]].user.displayName.len);
cborEncoder.encodeTextString(assertionCredentials[nextAssertion[0]].userEntity.displayName.str, (short) 0,
assertionCredentials[nextAssertion[0]].userEntity.displayName.len);
}
if (usrFlags[1]) {
// The 'id' tag
cborEncoder.encodeTextString(Utf8Strings.UTF8_ID, (short) 0, (short) 2);
cborEncoder.encodeByteString(assertionCreds[nextAssertion[0]].user.id, (short) 0,
(short) assertionCreds[nextAssertion[0]].user.id.length);
cborEncoder.encodeByteString(assertionCredentials[nextAssertion[0]].userEntity.id, (short) 0,
(short) assertionCredentials[nextAssertion[0]].userEntity.id.length);
}
if (usrFlags[0]) {
// The 'name'
cborEncoder.encodeTextString(Utf8Strings.UTF8_NAME, (short) 0, (short) 4);
cborEncoder.encodeTextString(assertionCreds[nextAssertion[0]].user.name.str, (short) 0,
assertionCreds[nextAssertion[0]].user.name.len);
cborEncoder.encodeTextString(assertionCredentials[nextAssertion[0]].userEntity.name.str, (short) 0,
assertionCredentials[nextAssertion[0]].userEntity.name.len);
}
if (usrFlags[3]) {
// Has the 'icon' tag
cborEncoder.encodeTextString(Utf8Strings.UTF8_ICON, (short) 0, (short) 4);
cborEncoder.encodeTextString(assertionCreds[nextAssertion[0]].user.icon, (short) 0,
(short) assertionCreds[nextAssertion[0]].user.icon.length);
cborEncoder.encodeTextString(assertionCredentials[nextAssertion[0]].userEntity.icon, (short) 0,
(short) assertionCredentials[nextAssertion[0]].userEntity.icon.length);
}
} else {
// UV not enabled. Don't send extra info apart from the id field
cborEncoder.startMap((short) 1);
cborEncoder.encodeTextString(Utf8Strings.UTF8_ID, (short) 0, (short) 2);
cborEncoder.encodeByteString(assertionCreds[nextAssertion[0]].user.id, (short) 0,
(short) assertionCreds[nextAssertion[0]].user.id.length);
cborEncoder.encodeByteString(assertionCredentials[nextAssertion[0]].userEntity.id, (short) 0,
(short) assertionCredentials[nextAssertion[0]].userEntity.id.length);
}
// Done tag 4
if (mapLen == 5) {
if (mapLength == 5) {
cborEncoder.encodeUInt8((byte) 0x05);
cborEncoder.encodeUInt8((byte) assertionCreds.length);
cborEncoder.encodeUInt8((byte) assertionCredentials.length);
}
}
......@@ -865,17 +817,17 @@ public class CTAP2 extends Applet implements ExtendedLength {
* I didn't want to pollute the logic over in the process function, and it makes
* sense to do both here.
*
* @param apdu
* @return length of data to be processed. 0 if command chaining's not finished.
* @param apdu apdu buffer
* @return length of data to be processed. 0 if command chaining is not finished.
*/
private short doApduIngestion(APDU apdu) {
byte[] buffer = apdu.getBuffer();
// Receive the APDU
vars[4] = apdu.setIncomingAndReceive();
tempVars[4] = apdu.setIncomingAndReceive();
// Get true incoming data length
vars[3] = apdu.getIncomingLength();
tempVars[3] = apdu.getIncomingLength();
// Check if the APDU is too big, we only handle 1200 byte
if (vars[3] > 1200) {
if (tempVars[3] > 1200) {
returnError(apdu, CTAP2_ERR_REQUEST_TOO_LARGE);
return 0;
}
......@@ -891,44 +843,44 @@ public class CTAP2 extends Applet implements ExtendedLength {
chainRam[0] = 0;
}
// Copy buffer
chainRam[1] = vars[4];
chainRam[1] = tempVars[4];
// chainRam[0] is the current point in the buffer we start from
chainRam[0] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inBuf, chainRam[0], chainRam[1]);
chainRam[0] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inputBuffer, chainRam[0], chainRam[1]);
return 0x00;
} else if (isChaining[0]) {
// Must be the last of the chaining - make the copy and return the length.
chainRam[1] = vars[4];
chainRam[0] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inBuf, chainRam[0], chainRam[1]);
chainRam[1] = tempVars[4];
chainRam[0] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inputBuffer, chainRam[0], chainRam[1]);
isChaining[0] = false;
isChaining[1] = true;
return chainRam[0];
} else if (vars[3] == 0x01) {
inBuf[0] = buffer[apdu.getOffsetCdata()];
} else if (tempVars[3] == 0x01) {
inputBuffer[0] = buffer[apdu.getOffsetCdata()];
return 0x01;
} else if (apdu.getCurrentState() == APDU.STATE_FULL_INCOMING) {
// We need to do no more
// Read the entirety of the buffer into the inBuf
Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inBuf, (short) 0, vars[3]);
return vars[4];
Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inputBuffer, (short) 0, tempVars[3]);
return tempVars[4];
} else {
// The APDU needs a multi-stage copy
// First, copy the current data buffer in
// Get the number of bytes in the data buffer that are the Lc, vars[5] will do
vars[5] = vars[4];
tempVars[5] = tempVars[4];
// Make the copy, vars[3] is bytes remaining to get
vars[4] = 0;
while (vars[3] > 0) {
tempVars[4] = 0;
while (tempVars[3] > 0) {
// Copy data
vars[4] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inBuf, vars[4], vars[5]);
tempVars[4] = Util.arrayCopyNonAtomic(buffer, apdu.getOffsetCdata(), inputBuffer, tempVars[4], tempVars[5]);
// Decrement vars[3] by the bytes copied
vars[3] -= vars[5];
tempVars[3] -= tempVars[5];
// Pull more bytes
vars[5] = apdu.receiveBytes(apdu.getOffsetCdata());
tempVars[5] = apdu.receiveBytes(apdu.getOffsetCdata());
}
// Now we're at the end, here, and the commands expect us to give them a data
// length. Turns out Le bytes aren't anywhere to be found here.
// The commands use vars[3], so vars[4] will be fine to copy to vars[3].
return vars[4];
return tempVars[4];
}
}
......@@ -936,14 +888,14 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Gets 256 or fewer bytes from inBuf.
*
* @param apdu
* @param apdu apdu buffer
*/
public void getData(APDU apdu) {
if (outChainRam[0] > 256) {
// More to go after this
outChainRam[0] -= 256;
byte[] buf = apdu.getBuffer();
Util.arrayCopyNonAtomic(inBuf, outChainRam[1], buf, (short) 0, (short) 256);
Util.arrayCopyNonAtomic(inputBuffer, outChainRam[1], buf, (short) 0, (short) 256);
apdu.setOutgoingAndSend((short) 0, (short) 256);
outChainRam[1] += 256;
if (outChainRam[0] > 255) {
......@@ -956,7 +908,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
} else {
// This is the last message
byte[] buf = apdu.getBuffer();
Util.arrayCopyNonAtomic(inBuf, outChainRam[1], buf, (short) 0, outChainRam[0]);
Util.arrayCopyNonAtomic(inputBuffer, outChainRam[1], buf, (short) 0, outChainRam[0]);
apdu.setOutgoingAndSend((short) 0, outChainRam[0]);
isOutChaining[0] = false;
outChainRam[0] = 0;
......@@ -969,7 +921,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
* Set chaining flags to send dataLen bytes from inLen via chaining, if
* necessary.
*
* @param apdu
* @param apdu apdu buffer
*/
public void sendLongChaining(APDU apdu, short dataLen) {
if (dataLen > 256) {
......@@ -980,7 +932,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
outChainRam[0] = (short) (dataLen - 256);
// Send the first 256 bytes out
byte[] buf = apdu.getBuffer();
Util.arrayCopyNonAtomic(inBuf, (short) 0, buf, (short) 0, (short) 256);
Util.arrayCopyNonAtomic(inputBuffer, (short) 0, buf, (short) 0, (short) 256);
apdu.setOutgoingAndSend((short) 0, (short) 256);
outChainRam[1] = 256;
// Throw the 61 xx
......@@ -996,7 +948,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
isOutChaining[0] = false;
apdu.setOutgoing();
apdu.setOutgoingLength(dataLen);
apdu.sendBytesLong(inBuf, (short) 0, dataLen);
apdu.sendBytesLong(inputBuffer, (short) 0, dataLen);
ISOException.throwIt(ISO7816.SW_NO_ERROR);
}
}
......@@ -1004,16 +956,16 @@ public class CTAP2 extends Applet implements ExtendedLength {
/**
* Checks if chaining is set for U2FApplet
*
* @return
* @return if it is chaining
*/
public boolean isChaining() {
return isOutChaining[0];
}
private void getCert(APDU apdu) {
inBuf[0] = 0x00;
vars[0] = (short) (attestationKeyPair.getCert(inBuf, (short) 1) + 1);
sendLongChaining(apdu, vars[0]);
inputBuffer[0] = 0x00;
tempVars[0] = (short) (attestationKeyPair.getCert(inputBuffer, (short) 1) + 1);
sendLongChaining(apdu, tempVars[0]);
}
public void process(APDU apdu) throws ISOException {
......
package com.josh.vku2f;
public class CTAP2ErrorCode {
public static final byte CTAP1_ERR_SUCCESS = (byte) 0x00;
public static final byte CTAP1_ERR_INVALID_COMMAND = (byte) 0x01;
public static final byte CTAP1_ERR_INVALID_PARAMETER = (byte) 0x02;
public static final byte CTAP1_ERR_INVALID_LENGTH = (byte) 0x03;
public static final byte CTAP1_ERR_INVALID_SEQ = (byte) 0x04;
public static final byte CTAP1_ERR_TIMEOUT = (byte) 0x05;
public static final byte CTAP1_ERR_CHANNEL_BUSY = (byte) 0x06;
public static final byte CTAP1_ERR_LOCK_REQUIRED = (byte) 0x0A;
public static final byte CTAP1_ERR_INVALID_CHANNEL = (byte) 0x0B;
public static final byte CTAP1_ERR_OTHER = (byte) 0x7F;
public static final byte CTAP2_ERR_CBOR_UNEXPECTED_TYPE = (byte) 0x11;
public static final byte CTAP2_ERR_INVALID_CBOR = (byte) 0x12;
public static final byte CTAP2_ERR_MISSING_PARAMETER = (byte) 0x14;
public static final byte CTAP2_ERR_LIMIT_EXCEEDED = (byte) 0x15;
public static final byte CTAP2_ERR_UNSUPPORTED_EXTENSION = (byte) 0x16;
public static final byte CTAP2_ERR_CREDENTIAL_EXCLUDED = (byte) 0x19;
public static final byte CTAP2_ERR_PROCESSING = (byte) 0x21;
public static final byte CTAP2_ERR_INVALID_CREDENTIAL = (byte) 0x22;
public static final byte CTAP2_ERR_USER_ACTION_PENDING = (byte) 0x23;
public static final byte CTAP2_ERR_OPERATION_PENDING = (byte) 0x24;
public static final byte CTAP2_ERR_NO_OPERATIONS = (byte) 0x25;
public static final byte CTAP2_ERR_UNSUPPORTED_ALGORITHM = (byte) 0x26;
public static final byte CTAP2_ERR_OPERATION_DENIED = (byte) 0x27;
public static final byte CTAP2_ERR_KEY_STORE_FULL = (byte) 0x28;
public static final byte CTAP2_ERR_NO_OPERATION_PENDING = (byte) 0x2A;
public static final byte CTAP2_ERR_UNSUPPORTED_OPTION = (byte) 0x2B;
public static final byte CTAP2_ERR_INVALID_OPTION = (byte) 0x2C;
public static final byte CTAP2_ERR_KEEPALIVE_CANCEL = (byte) 0x2D;
public static final byte CTAP2_ERR_NO_CREDENTIALS = (byte) 0x2E;
public static final byte CTAP2_ERR_USER_ACTION_TIMEOUT = (byte) 0x2F;
public static final byte CTAP2_ERR_NOT_ALLOWED = (byte) 0x30;
public static final byte CTAP2_ERR_PIN_INVALID = (byte) 0x31;
public static final byte CTAP2_ERR_PIN_BLOCKED = (byte) 0x32;
public static final byte CTAP2_ERR_PIN_AUTH_INVALID = (byte) 0x33;
public static final byte CTAP2_ERR_PIN_AUTH_BLOCKED = (byte) 0x34;
public static final byte CTAP2_ERR_PIN_NOT_SET = (byte) 0x35;
public static final byte CTAP2_ERR_PIN_REQUIRED = (byte) 0x36;
public static final byte CTAP2_ERR_PIN_POLICY_VIOLATION = (byte) 0x37;
public static final byte CTAP2_ERR_PIN_TOKEN_EXPIRED = (byte) 0x38;
public static final byte CTAP2_ERR_REQUEST_TOO_LARGE = (byte) 0x39;
public static final byte CTAP2_ERR_ACTION_TIMEOUT = (byte) 0x3A;
public static final byte CTAP2_ERR_UP_REQUIRED = (byte) 0x3B;
}
......@@ -18,38 +18,40 @@ package com.josh.vku2f;
import javacard.framework.JCSystem;
import javacard.framework.UserException;
import static com.josh.vku2f.CTAP2ErrorCode.*;
/**
* Dynamically resizable credential storage array. Gracefully handles space errors.
*/
public class CredentialArray {
private StoredCredential[] creds;
private StoredCredential[] credentials;
private boolean[] slotStatus;
private short size;
private short counter;
private short i; // for loop counter
private short count = 0;//the number of creds in the array
/**
* Constructor for a CredentialArray.
* @param initialSize Initial sizing for the CredentialArray.
*/
public CredentialArray(short initialSize) {
creds = new StoredCredential[initialSize];
credentials = new StoredCredential[initialSize];
slotStatus = new boolean[initialSize];
size = initialSize;
}
/**
* Adds a new credential to the first free slot, or overwrites if a matching rp and user id matches.
* @param in the StoredCredential object to be stored.
* @param newCredential the StoredCredential object to be stored.
*/
public void addCredential(StoredCredential in) throws UserException{
public void addCredential(StoredCredential newCredential) throws UserException{
try {
short slot = alreadyExists(in);
creds[slot] = in;
short slot = checkExists(newCredential);
credentials[slot] = newCredential;
slotStatus[slot] = true;
count = (short)(slot + 1);
} catch (Exception e) {
UserException.throwIt(CTAP2.CTAP2_ERR_KEY_STORE_FULL);
UserException.throwIt(CTAP2_ERR_KEY_STORE_FULL);
}
}
/**
......@@ -60,10 +62,10 @@ public class CredentialArray {
* @ return
*/
public StoredCredential getCredential(byte[] rpId, short rpOff, short rpLen, byte[] userId, short userOff, short userLen) {
for(counter = 0; counter < size; counter++) {
for(i = 0; i < size; i++) {
// Check the slot status, if the RP matches, and then if the user matches. If so, return the credential.
if(slotStatus[counter] && creds[counter].rp.checkId(rpId, rpOff, rpLen) && creds[counter].user.checkId(userId, userOff, userLen)) {
return creds[counter];
if(slotStatus[i] && credentials[i].rpEntity.checkId(rpId, rpOff, rpLen) && credentials[i].userEntity.checkId(userId, userOff, userLen)) {
return credentials[i];
}
}
return null;
......@@ -75,42 +77,43 @@ public class CredentialArray {
* Confirms there is no already existing discoverable credential - if it finds one, it returns its location for overwriting.
* @return the location of a discoverable credential already matching the RP and User IDs, or the first free slot otherwise.
*/
public short alreadyExists(StoredCredential cred) {
for(counter = 0; counter < size; counter++) {
public short checkExists(StoredCredential newCredential) {
for(i = 0; i < size; i++) {
// Check the slot status, if the RP matches, and then if the user matches. If so, return the slot to use.
if(slotStatus[counter] && creds[counter].rp.checkId(cred.rp) && creds[counter].user.checkId(cred.user)) {
return counter;
if(slotStatus[i] &&
credentials[i].rpEntity.checkId(newCredential.rpEntity) &&
credentials[i].userEntity.checkId(newCredential.userEntity)) {
return i;
}
}
// Find the first free slot
for(counter = 0; counter < size; counter++) {
if(!slotStatus[counter]) {
return counter;
for(i = 0; i < size; i++) {
if(!slotStatus[i]) {
return i;
}
}
// No free slots
// Add more
StoredCredential[] tmp = new StoredCredential[size];
boolean[] tmpStatus = new boolean[size];
for(counter = 0; counter < size; counter++) {
for(i = 0; i < size; i++) {
// SonarLint throws an error here, but JavaCard can only copy byte arrays
tmp[counter] = creds[counter];
tmpStatus[counter] = slotStatus[counter];
tmp[i] = credentials[i];
tmpStatus[i] = slotStatus[i];
}
creds = new StoredCredential[(short) (size*2)];
credentials = new StoredCredential[(short) (size*2)];
slotStatus = new boolean[(short) (size*2)];
for(counter = 0; counter < size; counter++) {
creds[counter] = tmp[counter];
slotStatus[counter] = tmpStatus[counter];
for(i = 0; i < size; i++) {
credentials[i] = tmp[i];
slotStatus[i] = tmpStatus[i];
}
// Actually double the size....
size *= (short) 2;
// Delete objects we used to copy
JCSystem.requestObjectDeletion();
// Return the first free slot in the new array, which is going to be the counter plus 1
return (short) (counter + (short) 1);
return ++i;
}
/**
* Get the size of the array.
......@@ -128,8 +131,8 @@ public class CredentialArray {
* @param position the position to get.
* @return the credential, or null.
*/
public StoredCredential getCred(short position) {
return creds[position];
public StoredCredential get(short position) {
return credentials[position];
}
}
......@@ -21,10 +21,10 @@ import javacard.security.RandomData;
/**
* Provide a way to handle static RNGs.
*/
public class ServerKeyCrypto {
public class Random {
private static RandomData rng;
public static RandomData getRng() {
public static RandomData getInstance() {
if(rng == null) {
rng = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
}
......
......@@ -23,27 +23,26 @@ import javacard.security.RandomData;
// Abstract class to represent and perform actions with a stored credential
public abstract class StoredCredential {
private static RandomData rng;
byte[] id;
KeyPair kp;
PublicKeyCredentialUserEntity user;
PublicKeyCredentialRpEntity rp;
private byte[] sigCounter;
private static RandomData randomData;
byte[] credentialId;
KeyPair keyPair;
PublicKeyCredentialUserEntity userEntity;
PublicKeyCredentialRpEntity rpEntity;
private final byte[] signingCounter;
protected boolean initialised;
protected byte[] credRandom;
protected boolean hmacEnabled;
protected StoredCredential() {
if(rng == null) {
rng = ServerKeyCrypto.getRng();
if(randomData == null) {
randomData = Random.getInstance();
}
id = new byte[16];
rng.generateData(id, (short) 0, (short) 16);
sigCounter = new byte[4];
credentialId = new byte[16];
randomData.generateData(credentialId, (short) 0, (short) 16);
signingCounter = new byte[4];
initialised = false;
hmacEnabled = false;
}
// Does the HMAC secret stuff
public short doHmacSecret(byte[] inBuf, short inOff, short inLen) {
......@@ -55,7 +54,7 @@ public abstract class StoredCredential {
public boolean initialiseCredSecret() {
// Generate the actual credRandom - this is the same across all credentials
credRandom = new byte[32];
rng.generateData(credRandom, (short) 0, (short) 32);
randomData.generateData(credRandom, (short) 0, (short) 32);
hmacEnabled = true;
return true;
}
......@@ -66,11 +65,11 @@ public abstract class StoredCredential {
if(inLen != (short) 16) {
return false;
}
return Util.arrayCompare(id, (short) 0, inBuf, inOff, inLen) == 0;
return Util.arrayCompare(credentialId, (short) 0, inBuf, inOff, inLen) == 0;
}
public boolean[] getPresentUser() {
return user.dataPresent;
return userEntity.dataPresent;
}
/**
* Increment the counter.
......@@ -80,20 +79,20 @@ public abstract class StoredCredential {
JCSystem.beginTransaction();
for(short i = 3; i > 1; i--) {
if(sigCounter[i] == 0xFF) {
sigCounter[(short) (i-1)]++;
sigCounter[i] = 0x00;
if(signingCounter[i] == 0xFF) {
signingCounter[(short) (i-1)]++;
signingCounter[i] = 0x00;
JCSystem.commitTransaction();
return;
}
}
if(sigCounter[0] == 0xFF && sigCounter[1] == 0xFF && sigCounter[2] == 0xFF && sigCounter[3] == 0xFF) {
if(signingCounter[0] == 0xFF && signingCounter[1] == 0xFF && signingCounter[2] == 0xFF && signingCounter[3] == 0xFF) {
// Overflow, roll to 0
Util.arrayFillNonAtomic(sigCounter, (short) 0, (short) 4, (byte) 0x00);
Util.arrayFillNonAtomic(signingCounter, (short) 0, (short) 4, (byte) 0x00);
JCSystem.commitTransaction();
return;
}
sigCounter[3]++;
signingCounter[3]++;
JCSystem.commitTransaction();
}
/**
......@@ -103,7 +102,7 @@ public abstract class StoredCredential {
* @returns length
*/
public short readCounter(byte[] buf, short bufOff) {
Util.arrayCopy(sigCounter, (short) 0, buf, bufOff, (short) 4);
Util.arrayCopy(signingCounter, (short) 0, buf, bufOff, (short) 4);
return (short) 4;
}
......@@ -145,7 +144,7 @@ public abstract class StoredCredential {
buf[(short) (off+16)] = 0x00;
buf[(short) (off+17)] = 0x10;
// Copy the credential ID
Util.arrayCopy(id, (short) 0, buf, (short) (off+18), (short) 16);
Util.arrayCopy(credentialId, (short) 0, buf, (short) (off+18), (short) 16);
}
}
......@@ -29,13 +29,13 @@ public class StoredES256Credential extends StoredCredential {
public StoredES256Credential(AuthenticatorMakeCredential inputData) {
// Generate a new ES256 credential
kp = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
KeyParams.sec256r1params((ECKey) kp.getPublic());
kp.genKeyPair();
user = inputData.getUser();
rp = inputData.getRp();
keyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
KeyParams.sec256r1params((ECKey) keyPair.getPublic());
keyPair.genKeyPair();
userEntity = inputData.getUser();
rpEntity = inputData.getRp();
sig = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
sig.init(kp.getPrivate(), Signature.MODE_SIGN);
sig.init(keyPair.getPrivate(), Signature.MODE_SIGN);
}
......@@ -65,7 +65,7 @@ public class StoredES256Credential extends StoredCredential {
w = new byte[65];
}
((ECPublicKey) kp.getPublic()).getW(w, (short) 0);
((ECPublicKey) keyPair.getPublic()).getW(w, (short) 0);
// Form the common params
doAttestationCommon(buf, off);
enc.init(buf, (short) (off + 34), (short) 1000);
......
......@@ -26,12 +26,12 @@ public class StoredPS256Credential extends StoredCredential {
public StoredPS256Credential(AuthenticatorMakeCredential inputData) {
// Generate a new RS256 credential
kp = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048);
kp.genKeyPair();
user = inputData.getUser();
rp = inputData.getRp();
keyPair = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048);
keyPair.genKeyPair();
userEntity = inputData.getUser();
rpEntity = inputData.getRp();
kpSignature = Signature.getInstance(Signature.ALG_RSA_SHA_256_PKCS1_PSS, false);
kpSignature.init(kp.getPrivate(), Signature.MODE_SIGN);
kpSignature.init(keyPair.getPrivate(), Signature.MODE_SIGN);
}
......@@ -69,12 +69,12 @@ public class StoredPS256Credential extends StoredCredential {
enc.encodeNegativeUInt8((byte) 0x00);
// Write the modulus
short start = enc.startByteString((short) 256);
((RSAPublicKey) kp.getPublic()).getModulus(buf, start);
((RSAPublicKey) keyPair.getPublic()).getModulus(buf, start);
// Exponent tag
enc.encodeNegativeUInt8((byte) 0x01);
// Write the exponent
start = enc.startByteString((short) 3);
((RSAPublicKey) kp.getPublic()).getExponent(buf, start);
((RSAPublicKey) keyPair.getPublic()).getExponent(buf, start);
return 305;
}
......
......@@ -26,12 +26,12 @@ public class StoredRS256Credential extends StoredCredential {
public StoredRS256Credential(AuthenticatorMakeCredential inputData) {
// Generate a new RS256 credential
kp = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048);
kp.genKeyPair();
user = inputData.getUser();
rp = inputData.getRp();
keyPair = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048);
keyPair.genKeyPair();
userEntity = inputData.getUser();
rpEntity = inputData.getRp();
kpSignature = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
kpSignature.init(kp.getPrivate(), Cipher.MODE_ENCRYPT);
kpSignature.init(keyPair.getPrivate(), Cipher.MODE_ENCRYPT);
}
......@@ -66,12 +66,12 @@ public class StoredRS256Credential extends StoredCredential {
enc.encodeNegativeUInt8((byte) 0x00);
// Write the modulus
short start = enc.startByteString((short) 256);
((RSAPublicKey) kp.getPublic()).getModulus(buf, start);
((RSAPublicKey) keyPair.getPublic()).getModulus(buf, start);
// Exponent tag
enc.encodeNegativeUInt8((byte) 0x01);
// Write the exponent
start = enc.startByteString((short) 3);
((RSAPublicKey) kp.getPublic()).getExponent(buf, start);
((RSAPublicKey) keyPair.getPublic()).getExponent(buf, start);
return 306;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment