Skip to content

Commit edf5d1d

Browse files
committed
Make sure wrappingKey is actually transient
1 parent df9ba31 commit edf5d1d

File tree

3 files changed

+63
-28
lines changed

3 files changed

+63
-28
lines changed

src/main/java/us/q3q/fido2/FIDO2Applet.java

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ private void makeCredential(APDU apdu, short lc, short readIdx) {
760760
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_OPERATION_DENIED);
761761
}
762762
}
763+
loadWrappingKeyIfNoPIN();
763764

764765
final short scratchRPIDHashOffset = scratchAlloc(RP_HASH_LEN);
765766
short digested = sha256.doFinal(bufferMem, rpIdIdx, rpIdLen, scratch, scratchRPIDHashOffset);
@@ -965,6 +966,18 @@ private void makeCredential(APDU apdu, short lc, short readIdx) {
965966
doSendResponse(apdu, outputLen);
966967
}
967968

969+
/**
970+
* If, and only if, no PIN is set, directly initialize symmetric crypto
971+
* from our flash-stored wrapping key (which should be unencrypted)
972+
*/
973+
private void loadWrappingKeyIfNoPIN() {
974+
if (!pinSet) {
975+
wrappingKey.setKey(wrappingKeySpace, (short) 0);
976+
977+
initSymmetricCrypto();
978+
}
979+
}
980+
968981
/**
969982
* Copies a section of a raw RP ID into a target buffer. If it is longer than the max it will be cut down.
970983
* If it is shorter, it will be zero-padded.
@@ -1433,39 +1446,19 @@ private void getAssertion(APDU apdu, short lc, short readIdx) {
14331446
byte[] matchingPubKeyBuffer = bufferMem;
14341447
short matchingPubKeyCredDataLen = 0;
14351448
short rkMatch = -1;
1449+
short allowListIdx = -1;
14361450

14371451
short paramsRead = 2;
14381452
if (bufferMem[readIdx] == 0x03) { // allowList
14391453
readIdx++;
1440-
paramsRead++;
1441-
if (readIdx >= lc) {
1442-
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_INVALID_CBOR);
1443-
}
1444-
1445-
if (((byte)(bufferMem[readIdx] & 0xF0)) != (byte)0x80) {
1446-
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
1447-
}
1448-
short numPubKeysToCheck = (short) (bufferMem[readIdx++] & 0x0F);
14491454
if (readIdx >= lc) {
14501455
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_INVALID_CBOR);
14511456
}
14521457

1453-
for (short i = 0; i < numPubKeysToCheck; i++) {
1454-
tempShorts[IDX_TEMP_BUF_IDX_STORAGE] = 0;
1455-
short beforeReadIdx = readIdx;
1456-
readIdx = consumeMapAndGetID(apdu, readIdx, lc);
1457-
short pubKeyIdx = tempShorts[IDX_TEMP_BUF_IDX_STORAGE];
1458-
short pubKeyLen = tempShorts[IDX_TEMP_BUF_IDX_LEN];
1459-
1460-
if (startOfMatchingPubKeyCredData == (short) -1) {
1461-
boolean matches = checkCredential(bufferMem, pubKeyIdx, pubKeyLen, scratch, scratchRPIDHashIdx,
1462-
privateScratch, (short) 0);
1463-
if (matches) {
1464-
startOfMatchingPubKeyCredData = beforeReadIdx;
1465-
matchingPubKeyCredDataLen = (short) (readIdx - startOfMatchingPubKeyCredData);
1466-
}
1467-
}
1468-
}
1458+
// We need to defer this until after we do PIN processing
1459+
allowListIdx = readIdx;
1460+
paramsRead++;
1461+
readIdx = consumeAnyEntity(apdu, readIdx, lc);
14691462
}
14701463

14711464
short hmacSecretBytes = 0;
@@ -1540,6 +1533,36 @@ private void getAssertion(APDU apdu, short lc, short readIdx) {
15401533
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_OPERATION_DENIED);
15411534
}
15421535

1536+
loadWrappingKeyIfNoPIN();
1537+
1538+
if (allowListIdx != -1) {
1539+
short blockReadIdx = allowListIdx;
1540+
if (((byte)(bufferMem[blockReadIdx] & 0xF0)) != (byte)0x80) {
1541+
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
1542+
}
1543+
short numPubKeysToCheck = (short) (bufferMem[blockReadIdx++] & 0x0F);
1544+
if (blockReadIdx >= lc) {
1545+
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_INVALID_CBOR);
1546+
}
1547+
1548+
for (short i = 0; i < numPubKeysToCheck; i++) {
1549+
tempShorts[IDX_TEMP_BUF_IDX_STORAGE] = 0;
1550+
short beforeReadIdx = blockReadIdx;
1551+
blockReadIdx = consumeMapAndGetID(apdu, blockReadIdx, lc);
1552+
short pubKeyIdx = tempShorts[IDX_TEMP_BUF_IDX_STORAGE];
1553+
short pubKeyLen = tempShorts[IDX_TEMP_BUF_IDX_LEN];
1554+
1555+
if (startOfMatchingPubKeyCredData == (short) -1) {
1556+
boolean matches = checkCredential(bufferMem, pubKeyIdx, pubKeyLen, scratch, scratchRPIDHashIdx,
1557+
privateScratch, (short) 0);
1558+
if (matches) {
1559+
startOfMatchingPubKeyCredData = beforeReadIdx;
1560+
matchingPubKeyCredDataLen = (short) (blockReadIdx - startOfMatchingPubKeyCredData);
1561+
}
1562+
}
1563+
}
1564+
}
1565+
15431566
if (startOfMatchingPubKeyCredData == -1) {
15441567
// Scan resident keys for match
15451568
for (short i = 0; i < NUM_RESIDENT_KEY_SLOTS; i++) {
@@ -3364,7 +3387,9 @@ private void rawSetPIN(APDU apdu, byte[] pinBuf, short offset, short pinLength)
33643387
JCSystem.beginTransaction();
33653388
boolean ok = false;
33663389
try {
3367-
wrappingKey.getKey(wrappingKeySpace, (short) 0);
3390+
if (pinSet) {
3391+
wrappingKey.getKey(wrappingKeySpace, (short) 0);
3392+
}
33683393

33693394
pinSet = true;
33703395
tempBools[IDX_RESET_PIN_PROVIDED] = false;
@@ -3611,7 +3636,7 @@ protected FIDO2Applet(byte[] array, short offset, byte length) {
36113636
wrappingKeyValidation = new byte[64];
36123637
hmacWrapperBytes = new byte[32];
36133638
wrappingIV = new byte[16];
3614-
wrappingKey = getPersistentAESKey(); // Our most important treasure, from which all other crypto is born...
3639+
wrappingKey = getTransientAESKey(); // Our most important treasure, from which all other crypto is born...
36153640
// Resident key data, of course, must all be in flash. Losing that on reset would be Bad
36163641
residentKeyData = new byte[NUM_RESIDENT_KEY_SLOTS * CREDENTIAL_ID_LEN];
36173642
residentKeyValidity = new boolean[NUM_RESIDENT_KEY_SLOTS];

src/test/java/MyTestHarness.java renamed to src/test/java/us/q3q/fido2/UnitTesting.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package us.q3q.fido2;
2+
13
import com.licel.jcardsim.smartcardio.CardSimulator;
24
import com.licel.jcardsim.utils.AIDUtil;
35

@@ -17,7 +19,10 @@
1719
import static org.junit.jupiter.api.Assertions.assertEquals;
1820
import static org.junit.jupiter.api.Assertions.assertNotEquals;
1921

20-
public class MyTestHarness {
22+
/**
23+
* Example (only example) unit tests with jcardsim
24+
*/
25+
public class UnitTesting {
2126

2227
CardSimulator simulator;
2328
AID appletAID = AIDUtil.create("F000000001");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
package us.q3q.fido2;
2+
13
import com.licel.jcardsim.remote.VSmartCard;
24
import com.licel.jcardsim.utils.AIDUtil;
35
import javacard.framework.AID;
46
import us.q3q.fido2.FIDO2Applet;
57

8+
/**
9+
* Launches jcardsim with VSmartCard connectivity
10+
*/
611
public class VSim {
712

813
static AID appletAID = AIDUtil.create("A0000006472F0001");

0 commit comments

Comments
 (0)