@@ -348,11 +348,11 @@ public final class FIDO2Applet extends Applet implements ExtendedLength {
348348 /**
349349 * Storage for the largeBlobs extension
350350 */
351- private byte [] largeBlobStore ;
351+ private final byte [][] largeBlobStores ;
352352 /**
353- * Double buffer for the large blob store
353+ * Which large blob store is in use
354354 */
355- private byte [] pendingLargeBlobStore ;
355+ private byte largeBlobStoreIndex = 0 ;
356356 /**
357357 * Length of the currently stored large-blob array
358358 */
@@ -3773,7 +3773,7 @@ private void handleLargeBlobs(APDU apdu, byte[] reqBuffer, short lc) {
37733773 sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
37743774 }
37753775
3776- if (offset < 0 || offset > (short ) largeBlobStore .length ) {
3776+ if (offset < 0 || offset > (short ) largeBlobStores [ largeBlobStoreIndex ] .length ) {
37773777 // Offset is mandatory and must be reasonable
37783778 sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
37793779 }
@@ -3804,7 +3804,7 @@ private void handleLargeBlobs(APDU apdu, byte[] reqBuffer, short lc) {
38043804 if (setTotalLength < 17 ) {
38053805 sendErrorByte (apdu , FIDOConstants .CTAP1_ERR_INVALID_PARAMETER );
38063806 }
3807- if (setTotalLength > (short ) largeBlobStore .length ) {
3807+ if (setTotalLength > (short ) largeBlobStores [ largeBlobStoreIndex ] .length ) {
38083808 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_LARGE_BLOB_STORAGE_FULL );
38093809 }
38103810 } else {
@@ -3875,8 +3875,9 @@ private void handleLargeBlobs(APDU apdu, byte[] reqBuffer, short lc) {
38753875 */
38763876 private void handleLargeBlobSet (APDU apdu , byte [] buffer , short incomingDataOffset ,
38773877 short blobWriteOffset , short incomingDataLength , short totalLength ) {
3878+ final byte tempBlobStoreIndex = (byte )(largeBlobStoreIndex == 0 ? 1 : 0 );
38783879 Util .arrayCopyNonAtomic (buffer , incomingDataOffset ,
3879- pendingLargeBlobStore , blobWriteOffset , incomingDataLength );
3880+ largeBlobStores [ tempBlobStoreIndex ] , blobWriteOffset , incomingDataLength );
38803881 // Done with incoming request now
38813882 bufferManager .informAPDUBufferAvailability (apdu , (short ) 0xFF );
38823883
@@ -3889,11 +3890,11 @@ private void handleLargeBlobSet(APDU apdu, byte[] buffer, short incomingDataOffs
38893890 byte [] scratch = bufferManager .getBufferForHandle (apdu , scratchHandle );
38903891 short scratchOffset = bufferManager .getOffsetForHandle (scratchHandle );
38913892
3892- sha256 .doFinal (pendingLargeBlobStore , (short ) 0 , (short )(totalLength - 16 ),
3893+ sha256 .doFinal (largeBlobStores [ tempBlobStoreIndex ] , (short ) 0 , (short )(totalLength - 16 ),
38933894 scratch , scratchOffset );
38943895
38953896 if (Util .arrayCompare (scratch , scratchOffset ,
3896- pendingLargeBlobStore , (short )(totalLength - 16 ), (short ) 16 ) != 0 ) {
3897+ largeBlobStores [ tempBlobStoreIndex ] , (short )(totalLength - 16 ), (short ) 16 ) != 0 ) {
38973898 // hash mismatch
38983899 sendErrorByte (apdu , FIDOConstants .CTAP2_ERR_INTEGRITY_FAILURE );
38993900 }
@@ -3904,9 +3905,7 @@ private void handleLargeBlobSet(APDU apdu, byte[] buffer, short incomingDataOffs
39043905 JCSystem .beginTransaction ();
39053906 boolean ok = false ;
39063907 try {
3907- byte [] temp = pendingLargeBlobStore ;
3908- pendingLargeBlobStore = largeBlobStore ;
3909- largeBlobStore = temp ;
3908+ largeBlobStoreIndex = tempBlobStoreIndex ;
39103909 largeBlobStoreFill = totalLength ;
39113910 } finally {
39123911 if (ok ) {
@@ -3953,7 +3952,7 @@ private void handleLargeBlobGet(APDU apdu, short numBytes, short offset) {
39533952 outBuffer [writeIdx ++] = (byte ) 0xA1 ; // map - one key
39543953 outBuffer [writeIdx ++] = (byte ) 0x01 ; // map key: config
39553954 writeIdx = encodeIntLenTo (outBuffer , writeIdx , bytesToRetrieve , true );
3956- writeIdx = Util .arrayCopyNonAtomic (largeBlobStore , offset ,
3955+ writeIdx = Util .arrayCopyNonAtomic (largeBlobStores [ largeBlobStoreIndex ] , offset ,
39573956 outBuffer , writeIdx , bytesToRetrieve );
39583957
39593958 if (outBuffer == bufferMem ) {
@@ -5370,10 +5369,35 @@ private void authenticatorReset(APDU apdu) {
53705369 }
53715370 }
53725371
5373- short pinIdx = pinRetryCounter .prepareIndex ();
5372+ final short pinIdx = pinRetryCounter .prepareIndex ();
5373+ final byte tempBlobStoreIndex = (byte )(largeBlobStoreIndex == 0 ? 1 : 0 );
5374+ final byte realBlobStoreIndex = largeBlobStoreIndex ;
53745375
5376+ // Empty the large blob store OUTside the main transaction, since it's non-precious and double buffered
5377+ Util .arrayFillNonAtomic (largeBlobStores [tempBlobStoreIndex ], (short ) 0 ,
5378+ (short ) largeBlobStores [tempBlobStoreIndex ].length , (byte ) 0x00 );
5379+ Util .arrayCopyNonAtomic (CannedCBOR .INITIAL_LARGE_BLOB_ARRAY , (short ) 0 ,
5380+ largeBlobStores [tempBlobStoreIndex ], (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
53755381 JCSystem .beginTransaction ();
53765382 boolean ok = false ;
5383+ try {
5384+ largeBlobStoreIndex = tempBlobStoreIndex ;
5385+ largeBlobStoreFill = (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length ;
5386+ ok = true ;
5387+ } finally {
5388+ if (ok ) {
5389+ JCSystem .commitTransaction ();
5390+ } else {
5391+ JCSystem .abortTransaction ();
5392+ }
5393+ }
5394+ Util .arrayFillNonAtomic (largeBlobStores [realBlobStoreIndex ], (short ) 0 ,
5395+ (short ) largeBlobStores [realBlobStoreIndex ].length , (byte ) 0x00 );
5396+ Util .arrayCopyNonAtomic (CannedCBOR .INITIAL_LARGE_BLOB_ARRAY , (short ) 0 ,
5397+ largeBlobStores [realBlobStoreIndex ], (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
5398+
5399+ JCSystem .beginTransaction ();
5400+ ok = false ;
53775401 try {
53785402 random .generateData (hmacWrapperBytesUV , (short ) 0 , (short ) hmacWrapperBytesUV .length );
53795403 random .generateData (hmacWrapperBytesNoUV , (short ) 0 , (short ) hmacWrapperBytesNoUV .length );
@@ -5388,10 +5412,6 @@ private void authenticatorReset(APDU apdu) {
53885412 alwaysUv = FORCE_ALWAYS_UV ;
53895413 enterpriseAttestation = false ;
53905414 pinRetryCounter .reset (pinIdx );
5391- Util .arrayFillNonAtomic (largeBlobStore , (short ) 0 , (short ) largeBlobStore .length , (byte ) 0x00 );
5392- Util .arrayCopyNonAtomic (CannedCBOR .INITIAL_LARGE_BLOB_ARRAY , (short ) 0 ,
5393- largeBlobStore , (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
5394- largeBlobStoreFill = (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length ;
53955415 Util .arrayFillNonAtomic (minPinRPIDs , (short ) 0 , (short ) (MAX_RP_IDS_MIN_PIN_LENGTH * RP_HASH_LEN ), (byte ) 0x00 );
53965416
53975417 random .generateData (pinKDFSalt , (short ) 0 , (short ) pinKDFSalt .length );
@@ -5574,7 +5594,7 @@ private void sendAuthInfo(APDU apdu) {
55745594
55755595 buffer [offset ++] = 0x0B ; // map key: maxSerializedLargeBlobArray: 1 byte = 5
55765596 buffer [offset ++] = 0x19 ; // two-byte integer: 1 byte = 6
5577- offset = Util .setShort (buffer , offset , (short ) largeBlobStore .length ); // 2 bytes = 8
5597+ offset = Util .setShort (buffer , offset , (short ) largeBlobStores [ largeBlobStoreIndex ] .length ); // 2 bytes = 8
55785598
55795599 buffer [offset ++] = 0x0C ; // map key: forcePinChange: 1 byte = 9
55805600 buffer [offset ++] = (byte )(forcePinChange ? 0xF5 : 0xF4 ); // 1 byte = 10
@@ -6798,10 +6818,11 @@ private FIDO2Applet(byte[] array, short offset, byte length) {
67986818 highSecurityWrappingIV = new byte [IV_LEN ];
67996819 lowSecurityWrappingIV = new byte [IV_LEN ];
68006820 externalCredentialIV = new byte [IV_LEN ];
6801- largeBlobStore = new byte [largeBlobStoreSize ];
6802- pendingLargeBlobStore = new byte [largeBlobStoreSize ];
6821+ largeBlobStores = new byte [2 ][largeBlobStoreSize ];
6822+ Util .arrayCopyNonAtomic (CannedCBOR .INITIAL_LARGE_BLOB_ARRAY , (short ) 0 ,
6823+ largeBlobStores [0 ], (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
68036824 Util .arrayCopyNonAtomic (CannedCBOR .INITIAL_LARGE_BLOB_ARRAY , (short ) 0 ,
6804- largeBlobStore , (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
6825+ largeBlobStores [ 1 ] , (short ) 0 , (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length );
68056826 largeBlobStoreFill = (short ) CannedCBOR .INITIAL_LARGE_BLOB_ARRAY .length ;
68066827 highSecurityWrappingKey = getTransientAESKey (); // Our most important treasure, from which all other crypto is born...
68076828 lowSecurityWrappingKey = getPersistentAESKey (); // Not really a treasure
0 commit comments