@@ -571,6 +571,7 @@ private void makeCredential(APDU apdu, short lc, short readIdx) {
571571
572572 numExcludeListEntries = (short )(excludeListTypeVal - 0x80 );
573573 excludeListStartIdx = (short )(readIdx + 2 );
574+ readIdx ++;
574575 } else if (bufferMem [readIdx ] == 0x06 ) { // extensions
575576 if ((bufferMem [++readIdx ] & 0xA0 ) != 0xA0 ) {
576577 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
@@ -694,7 +695,7 @@ private void makeCredential(APDU apdu, short lc, short readIdx) {
694695 continue ;
695696 }
696697
697- if (checkCredential (bufferMem , credIdIdx , credIdLen ,
698+ if (checkCredential (bufferMem , credIdIdx , CREDENTIAL_ID_LEN ,
698699 scratch , scratchRPIDHashOffset ,
699700 scratch , excludeListCredSpace )) {
700701 // This cred decodes valid and is for the given RPID - fail early.
@@ -1620,6 +1621,13 @@ private void getAssertion(APDU apdu, short lc, short readIdx, short firstCredIdx
16201621 continue ;
16211622 }
16221623
1624+ if (CTAP_2_1_GETASSERTION_WITH_ALLOW_LIST && startOfMatchingPubKeyCredData != -1 ) {
1625+ // In CTAP2.1, we are done, as we're supposed to treat the match we already made as the only match
1626+ // we're just continuing iteration to throw exceptions when we encounter invalid allowList entries
1627+ // AFTER the valid one
1628+ continue ;
1629+ }
1630+
16231631 if (startOfMatchingPubKeyCredData == (short ) -1 || firstCredIdx == 0 ) {
16241632 // We need to check all the creds in the list if we're starting iteration...
16251633 // ... in order to count the number of matches (for CTAP2.0 only!)
@@ -1632,11 +1640,6 @@ private void getAssertion(APDU apdu, short lc, short readIdx, short firstCredIdx
16321640 startOfMatchingPubKeyCredData = beforeReadIdx ;
16331641 matchingPubKeyCredDataLen = (short ) (blockReadIdx - startOfMatchingPubKeyCredData );
16341642 loadPrivateScratchIntoAttester ();
1635-
1636- if (CTAP_2_1_GETASSERTION_WITH_ALLOW_LIST ) {
1637- // In CTAP2.1, we are done, as we're supposed to treat this match as the only match
1638- break ;
1639- }
16401643 }
16411644 }
16421645 }
@@ -2265,10 +2268,10 @@ private void setupChainedResponse(short offset, short remaining) {
22652268 transientStorage .setOutgoingContinuation (offset , remaining );
22662269 if (remaining >= 256 ) {
22672270 // at least ANOTHER full chunk remains
2268- throwException (ISO7816 .SW_BYTES_REMAINING_00 );
2271+ throwException (ISO7816 .SW_BYTES_REMAINING_00 , false );
22692272 } else {
22702273 // exactly one more chunk remains
2271- throwException ((short ) (ISO7816 .SW_BYTES_REMAINING_00 + remaining ));
2274+ throwException ((short ) (ISO7816 .SW_BYTES_REMAINING_00 + remaining ), false );
22722275 }
22732276 }
22742277
@@ -2417,14 +2420,14 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
24172420 boolean foundType = false ;
24182421 boolean correctType = false ;
24192422 transientStorage .readyStoredVars ();
2420- short mapDef = bufferMem [readIdx ++];
2423+ short mapDef = ub ( bufferMem [readIdx ++]) ;
24212424 if (readIdx >= lc ) {
24222425 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_INVALID_CBOR );
24232426 }
24242427 short mapEntryCount = 0 ;
24252428 if ((mapDef & 0xF0 ) == 0xA0 ) {
24262429 mapEntryCount = (short ) (mapDef & 0x0F );
2427- } else if ((mapDef & 0xF0 ) == 0xB0 ) {
2430+ } else if ((mapDef & 0xF0 ) == 0xB0 && mapDef < ub (( byte ) 0xB8 ) ) {
24282431 mapEntryCount = (short ) ((mapDef & 0x0F ) + 16 );
24292432 } else if (mapDef == (byte ) 0xB8 ) {
24302433 mapEntryCount = ub (bufferMem [readIdx ++]);
@@ -2436,7 +2439,7 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
24362439 }
24372440
24382441 for (short i = 0 ; i < mapEntryCount ; i ++) {
2439- short keyDef = bufferMem [readIdx ++];
2442+ short keyDef = ub ( bufferMem [readIdx ++]) ;
24402443 short keyLen = 0 ;
24412444 if (readIdx >= lc ) {
24422445 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_INVALID_CBOR );
@@ -2447,7 +2450,7 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
24472450 if (readIdx >= lc ) {
24482451 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_INVALID_CBOR );
24492452 }
2450- } else if (keyDef >= 0x61 && keyDef < 0x78 ) {
2453+ } else if (keyDef >= 0x60 && keyDef < 0x78 ) {
24512454 keyLen = (short )(keyDef - 0x60 );
24522455 } else {
24532456 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
@@ -2484,7 +2487,9 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
24842487 }
24852488 if (isType && valDef == 0x58 ) {
24862489 // heh, literally "unexpected type" here
2487- sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
2490+ // don't throw an exception, since the spec allows all sorts of "types", we just ignore them
2491+ foundType = true ;
2492+ correctType = false ;
24882493 }
24892494 valLen = ub (bufferMem [readIdx ++]);
24902495 if (isId ) {
@@ -2493,15 +2498,20 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
24932498 if (readIdx >= lc ) {
24942499 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_INVALID_CBOR );
24952500 }
2496- } else if (valDef >= 0x61 && valDef < 0x78 ) {
2501+ } else if (valDef >= 0x60 && valDef < 0x78 ) {
24972502 if (isId && byteString ) {
24982503 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
24992504 }
25002505 valLen = (short ) (valDef - 0x60 );
2501- } else if (valDef >= 0x41 && valDef < 0x58 ) {
2502- if (( isId && !byteString ) || isType ) {
2506+ } else if (valDef >= 0x40 && valDef < 0x58 ) {
2507+ if (isId && !byteString ) {
25032508 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
25042509 }
2510+ if (isType ) {
2511+ // byte strings aren't valid for public-key types...
2512+ foundType = true ;
2513+ correctType = false ;
2514+ }
25052515 valLen = (short ) (valDef - 0x40 );
25062516 } else {
25072517 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
@@ -2512,11 +2522,11 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
25122522 transientStorage .setStoredVars (idPos , valLen );
25132523 }
25142524
2515- if (isType && checkTypePublicKey ) {
2525+ if (! foundType && isType && checkTypePublicKey ) {
25162526 foundType = true ;
25172527 correctType = valLen == (short ) CannedCBOR .PUBLIC_KEY_TYPE .length
2518- || Util .arrayCompare (bufferMem , readIdx ,
2519- CannedCBOR .PUBLIC_KEY_TYPE , (short ) 0 , ( short ) CannedCBOR . PUBLIC_KEY_TYPE . length ) == 0 ;
2528+ && Util .arrayCompare (bufferMem , readIdx ,
2529+ CannedCBOR .PUBLIC_KEY_TYPE , (short ) 0 , valLen ) == 0 ;
25202530 }
25212531
25222532 readIdx += valLen ;
@@ -2529,16 +2539,19 @@ private short consumeMapAndGetID(APDU apdu, short readIdx, short lc, boolean byt
25292539 sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
25302540 }
25312541
2532- if (checkTypePublicKey && !foundType ) {
2533- // entirely missing "type" field
2534- sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
2535- }
2542+ if (checkTypePublicKey ) {
2543+ if (!foundType ) {
2544+ // entirely missing "type" field
2545+ sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
2546+ }
25362547
2537- if (checkTypePublicKey && foundType && !correctType ) {
2538- // We found an ID entry, but it's not of type "public-key" - treat it as if we found nothing
2539- transientStorage .readyStoredVars ();
2548+ if (!correctType ) {
2549+ // We found an ID entry, but it's not of type "public-key" - treat it as if we found nothing
2550+ transientStorage .readyStoredVars ();
2551+ }
25402552 }
25412553
2554+
25422555 return readIdx ;
25432556 }
25442557
@@ -3007,7 +3020,7 @@ private void handleDeleteCred(APDU apdu, short readOffset, short lc) {
30073020 // we want ANOTHER RK, not this one...
30083021 continue ;
30093022 }
3010- if (!residentKeyValidity [i ]) {
3023+ if (!residentKeyValidity [otherRKIdx ]) {
30113024 // deleted keys need not apply
30123025 continue ;
30133026 }
@@ -3503,7 +3516,12 @@ private void sendAuthInfo(APDU apdu) {
35033516 * @param swCode Two-byte status code - may be SW_NO_ERROR if desired
35043517 */
35053518 private void throwException (short swCode ) {
3506- transientStorage .clearIterationPointers ();
3519+ throwException (swCode , true );
3520+ }
3521+ private void throwException (short swCode , boolean clearIteration ) {
3522+ if (clearIteration ) {
3523+ transientStorage .clearIterationPointers ();
3524+ }
35073525 scratchRelease ();
35083526 ecPrivateKey .clearKey ();
35093527 ecKeyPair .getPrivate ().clearKey ();
@@ -3586,12 +3604,16 @@ private void handleClientPinChange(APDU apdu, short readIdx, short lc, byte pinP
35863604 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_NOT_SET );
35873605 }
35883606
3589- short scratchOff = scratchAlloc ((short ) (PIN_PAD_LENGTH + 16 )); // 16 bytes for PIN partial hash
3607+ // 32 bytes for PIN hash (for PIN protocol 2) and 16 for IV on padded PIN
3608+ short scratchOff = scratchAlloc ((short ) (PIN_PAD_LENGTH + 48 ));
35903609
35913610 readIdx = handlePinSetPreamble (apdu , readIdx , lc , scratch , scratchOff , true , pinProtocol );
35923611
35933612 short wrappedPinLocation = readIdx ;
35943613 readIdx += PIN_PAD_LENGTH ;
3614+ if (pinProtocol == 2 ) {
3615+ readIdx += 16 ; // IV for PIN pad
3616+ }
35953617
35963618 if (bufferMem [readIdx ++] != 0x06 ) { // pinHashEnc
35973619 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_MISSING_PARAMETER );
@@ -3641,6 +3663,12 @@ private void handleClientPinGetToken(APDU apdu, short readIdx, short lc, byte pi
36413663 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_NOT_SET );
36423664 }
36433665
3666+ if (transientStorage .getPinTriesSinceReset () == PIN_TRIES_PER_RESET ) {
3667+ // Proceed no further: PIN is blocked until authenticator is powered off and on again
3668+ // NO TOKEN FOR YOU. NEXT!
3669+ sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_AUTH_BLOCKED );
3670+ }
3671+
36443672 if (bufferMem [readIdx ++] != 0x03 ) { // map key: keyAgreement
36453673 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_MISSING_PARAMETER );
36463674 }
@@ -3897,6 +3925,10 @@ private void testAndReadyPIN(APDU apdu, byte[] buf, short off, byte pinProtocol,
38973925
38983926 // BAD PIN
38993927 forceInitKeyAgreementKey ();
3928+ if (transientStorage .getPinTriesSinceReset () == PIN_TRIES_PER_RESET ) {
3929+ // You've gone and done it now. Need to power the authenticator off and on again to try again.
3930+ sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_AUTH_BLOCKED );
3931+ }
39003932 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_INVALID );
39013933 }
39023934
@@ -3980,15 +4012,30 @@ private short handlePinSetPreamble(APDU apdu, short readIdx, short lc, byte[] ou
39804012 if (bufferMem [readAheadIdx ++] != 0x06 ) { // pinHashEnc
39814013 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_REQUIRED );
39824014 }
3983- if (bufferMem [readAheadIdx ++] != 0x50 ) { // byte array, 16 bytes long
3984- sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
3985- }
3986- Util .arrayCopyNonAtomic (bufferMem , readAheadIdx ,
3987- outBuf , (short )(outOffset + bLen ), (short ) 16 );
4015+ if (pinProtocol == 1 ) {
4016+ if (bufferMem [readAheadIdx ++] != 0x50 ) { // byte array, 16 bytes long
4017+ sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
4018+ }
4019+ Util .arrayCopyNonAtomic (bufferMem , readAheadIdx ,
4020+ outBuf , (short )(outOffset + bLen ), (short ) 16 );
39884021
3989- hmacSha256 (apdu , sharedSecretVerifyKey , (short ) 0 ,
3990- outBuf , outOffset , (short )(bLen + 16 ),
3991- outBuf , outOffset , false );
4022+ hmacSha256 (apdu , sharedSecretVerifyKey , (short ) 0 ,
4023+ outBuf , outOffset , (short )(bLen + 16 ),
4024+ outBuf , outOffset , false );
4025+ } else {
4026+ if (bufferMem [readAheadIdx ++] != 0x58 ) { // byte string, one-byte length
4027+ sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
4028+ }
4029+ if (bufferMem [readAheadIdx ++] != 0x20 ) { // 32: sixteen bytes of IV and 16 bytes of hash
4030+ sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_CBOR_UNEXPECTED_TYPE );
4031+ }
4032+ Util .arrayCopyNonAtomic (bufferMem , readAheadIdx ,
4033+ outBuf , (short )(outOffset + bLen ), (short ) 32 );
4034+
4035+ hmacSha256 (apdu , sharedSecretVerifyKey , (short ) 0 ,
4036+ outBuf , outOffset , (short )(bLen + 32 ),
4037+ outBuf , outOffset , false );
4038+ }
39924039 } else {
39934040 hmacSha256 (apdu , sharedSecretVerifyKey , (short ) 0 ,
39944041 bufferMem , readIdx , bLen ,
@@ -4032,7 +4079,7 @@ private void handleClientPinInitialSet(APDU apdu, short readIdx, short lc, byte
40324079 break ;
40334080 }
40344081 }
4035- if (realPinLength < 4 ) {
4082+ if (realPinLength < 4 || realPinLength > 63 ) {
40364083 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_PIN_POLICY_VIOLATION );
40374084 }
40384085
0 commit comments