@@ -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 ];
0 commit comments