@@ -3038,7 +3038,11 @@ private void doSendResponse(APDU apdu, short outputLen) {
30383038 amountFromMem = outputLen ;
30393039 }
30403040
3041- apdu .setOutgoingLength (amountFitInBuffer );
3041+ if (isExtendedAPDU ) {
3042+ apdu .setOutgoingLength (totalOutputLen );
3043+ } else {
3044+ apdu .setOutgoingLength (amountFitInBuffer );
3045+ }
30423046 final byte [] apduBytes = apdu .getBuffer ();
30433047 Util .arrayCopyNonAtomic (bufferMem , (short ) 0 ,
30443048 apduBytes , (short ) 0 , amountFromMem );
@@ -3067,8 +3071,14 @@ private void doSendResponse(APDU apdu, short outputLen) {
30673071 }
30683072 apdu .sendBytes ((short ) 0 , amountFitInBuffer );
30693073 if (totalOutputLen > amountFitInBuffer ) {
3070- // we're not done; set state to continue response delivery later
3071- setupChainedResponse (amountFitInBuffer , (short )(totalOutputLen - amountFitInBuffer ));
3074+ // we're not done; set state to continue response delivery later, with chaining
3075+ if (isExtendedAPDU ) {
3076+ transientStorage .setOutgoingContinuation (amountFitInBuffer , (short )(totalOutputLen - amountFitInBuffer ));
3077+ // deliver it right now, by repeatedly overwriting the APDU buffer
3078+ while (!streamOutgoingContinuation (apdu , apduBytes , false ));
3079+ } else {
3080+ setupChainedResponse (amountFitInBuffer , (short )(totalOutputLen - amountFitInBuffer ));
3081+ }
30723082 }
30733083 }
30743084
@@ -3397,7 +3407,7 @@ public void process(APDU apdu) throws ISOException {
33973407 }
33983408
33993409 if (cla_ins == 0x00C0 || cla_ins == (short ) 0x80C0 ) {
3400- streamOutgoingContinuation (apdu , apduBytes );
3410+ streamOutgoingContinuation (apdu , apduBytes , true );
34013411 return ;
34023412 } else {
34033413 if (transientStorage .getLargeBlobWriteOffset () == -1 ) {
@@ -4141,12 +4151,14 @@ private void u2FRegister(APDU apdu) {
41414151 *
41424152 * @param apdu Request/response context object
41434153 * @param apduBytes APDU buffer to hold outgoing data
4154+ * @param chaining If true, set the APDU outgoing length, and throw an exception to indicate remaining bytes
4155+ * @return true if response delivery is complete
41444156 */
4145- private void streamOutgoingContinuation (APDU apdu , byte [] apduBytes ) {
4157+ private boolean streamOutgoingContinuation (APDU apdu , byte [] apduBytes , boolean chaining ) {
41464158 // continue outgoing response from buffer
41474159 if (transientStorage .getOutgoingContinuationRemaining () == 0 ) {
41484160 // Nothing to send here, nothing left.
4149- return ;
4161+ return true ;
41504162 }
41514163
41524164 short outgoingOffset = transientStorage .getOutgoingContinuationOffset ();
@@ -4180,7 +4192,9 @@ private void streamOutgoingContinuation(APDU apdu, byte[] apduBytes) {
41804192 }
41814193
41824194 final short writeSize = chunkSize <= outgoingRemaining ? chunkSize : outgoingRemaining ;
4183- apdu .setOutgoingLength (writeSize );
4195+ if (chaining ) {
4196+ apdu .setOutgoingLength (writeSize );
4197+ }
41844198 short chunkToWrite = writeSize ;
41854199 if (remainingValidInBufMem > 0 ) {
41864200 short writeFromBufMem = remainingValidInBufMem ;
@@ -4208,12 +4222,15 @@ private void streamOutgoingContinuation(APDU apdu, byte[] apduBytes) {
42084222 outgoingOffset += writeSize ;
42094223 outgoingRemaining -= writeSize ;
42104224 transientStorage .setOutgoingContinuation (outgoingOffset , outgoingRemaining );
4211- if (outgoingRemaining >= 256 ) {
4212- throwException (ISO7816 .SW_BYTES_REMAINING_00 );
4213- } else if (outgoingRemaining > 0 ) {
4214- throwException ((short ) (ISO7816 .SW_BYTES_REMAINING_00 + outgoingRemaining ));
4225+ if (chaining ) {
4226+ if (outgoingRemaining >= 256 ) {
4227+ throwException (ISO7816 .SW_BYTES_REMAINING_00 );
4228+ } else if (outgoingRemaining > 0 ) {
4229+ throwException ((short ) (ISO7816 .SW_BYTES_REMAINING_00 + outgoingRemaining ));
4230+ }
4231+ transientStorage .clearOutgoingContinuation ();
42154232 }
4216- transientStorage . clearOutgoingContinuation () ;
4233+ return false ;
42174234 }
42184235
42194236 /**
@@ -5505,51 +5522,75 @@ private void sendAuthInfo(APDU apdu) {
55055522 buffer [offset ++] = 0x0A ; // ten
55065523
55075524 final short amountInApduBuf = offset ;
5525+
5526+ final short availableMem = JCSystem .getAvailableMemory (JCSystem .MEMORY_TYPE_PERSISTENT );
5527+ final short approximateKeyCount = (short )(availableMem / 128 );
5528+ boolean partiallySent = false ;
55085529 if (!longResponse ) {
55095530 // We're going to have too much for one 256-byte buffer
55105531 // So let's split into two halves, one directly APDU-written and one saved
55115532 buffer = bufferMem ;
55125533 offset = 0 ;
5534+ } else if (apdu .getBuffer ().length > 300 ) {
5535+ // We have an E-APDU, but there's room for our reply in the
5536+ // APDU outgoing buffer: keep writing
5537+ } else {
5538+ // We have an E-APDU, buf there's not room for our response in the outgoing
5539+ // buffer. Write aside.
5540+ final short remainingAmountToWrite = (short )(CannedCBOR .ES256_ALG_TYPE .length
5541+ + (approximateKeyCount > 23 ? 2 : 1 ) // encoded length of approxKeyCount
5542+ + (minPinLength > 23 ? 2 : 1 ) // encoded length of minPinLength
5543+ + 23 // fixed overhead;
5544+ );
5545+ apdu .setOutgoing ();
5546+ apdu .setOutgoingLength ((short )(offset + remainingAmountToWrite ));
5547+ apdu .sendBytes ((short ) 0 , offset );
5548+ // And overwrite the same buffer again
5549+ offset = 0 ;
5550+ partiallySent = true ;
55135551 }
55145552
5515- buffer [offset ++] = 0x08 ; // map key: maxCredentialIdLength
5516- offset = encodeIntTo (buffer , offset , (byte ) CREDENTIAL_ID_LEN );
5553+ buffer [offset ++] = 0x08 ; // map key: maxCredentialIdLength: 1 byte
5554+ offset = encodeIntTo (buffer , offset , (byte ) CREDENTIAL_ID_LEN ); // 2 bytes = 3
55175555
5518- buffer [offset ++] = 0x0A ; // map key: algorithms
5556+ buffer [offset ++] = 0x0A ; // map key: algorithms: 1 byte = 4
55195557 offset = Util .arrayCopyNonAtomic (CannedCBOR .ES256_ALG_TYPE , (short ) 0 ,
5520- buffer , offset , (short ) CannedCBOR .ES256_ALG_TYPE .length );
5558+ buffer , offset , (short ) CannedCBOR .ES256_ALG_TYPE .length ); // added separately
55215559
5522- buffer [offset ++] = 0x0B ; // map key: maxSerializedLargeBlobArray
5523- buffer [offset ++] = 0x19 ; // two-byte integer
5524- Util .setShort (buffer , offset , (short ) largeBlobStore .length );
5560+ buffer [offset ++] = 0x0B ; // map key: maxSerializedLargeBlobArray: 1 byte = 5
5561+ buffer [offset ++] = 0x19 ; // two-byte integer: 1 byte = 6
5562+ Util .setShort (buffer , offset , (short ) largeBlobStore .length ); // 2 bytes = 8
55255563 offset += 2 ;
55265564
5527- buffer [offset ++] = 0x0C ; // map key: forcePinChange
5528- buffer [offset ++] = (byte )(forcePinChange ? 0xF5 : 0xF4 );
5565+ buffer [offset ++] = 0x0C ; // map key: forcePinChange: 1 byte = 9
5566+ buffer [offset ++] = (byte )(forcePinChange ? 0xF5 : 0xF4 ); // 1 byte = 10
55295567
5530- buffer [offset ++] = 0x0D ; // map key: minPinLength
5531- offset = encodeIntTo (buffer , offset , minPinLength );
5568+ buffer [offset ++] = 0x0D ; // map key: minPinLength: 1 byte = 11
5569+ offset = encodeIntTo (buffer , offset , minPinLength ); // encoded separately
55325570
5533- buffer [offset ++] = 0x0E ; // map key: firmwareVersion
5534- offset = encodeIntTo (buffer , offset , FIRMWARE_VERSION );
5571+ buffer [offset ++] = 0x0E ; // map key: firmwareVersion: 1 byte = 12
5572+ offset = encodeIntTo (buffer , offset , FIRMWARE_VERSION ); // 1 byte = 13
55355573
5536- buffer [offset ++] = 0x0F ; // map key: maxCredBlobLength
5537- offset = encodeIntTo (buffer , offset , MAX_CRED_BLOB_LEN );
5574+ buffer [offset ++] = 0x0F ; // map key: maxCredBlobLength: 1 byte = 14
5575+ offset = encodeIntTo (buffer , offset , MAX_CRED_BLOB_LEN ); // 2 bytes = 16
55385576
5539- buffer [offset ++] = 0x10 ; // map key: maxRPIDsForSetMinPinLength
5540- offset = encodeIntTo (buffer , offset , MAX_RP_IDS_MIN_PIN_LENGTH );
5577+ buffer [offset ++] = 0x10 ; // map key: maxRPIDsForSetMinPinLength: 1 byte = 17
5578+ offset = encodeIntTo (buffer , offset , MAX_RP_IDS_MIN_PIN_LENGTH ); // 1 byte = 18
55415579
5542- buffer [offset ++] = 0x12 ; // map key: uvModality
5543- buffer [offset ++] = 0x19 ; // two-byte integer
5544- offset = Util .setShort (buffer , offset , (short ) 0x0200 ); // uvModality "none"*/
5580+ buffer [offset ++] = 0x12 ; // map key: uvModality: 1 byte = 19
5581+ buffer [offset ++] = 0x19 ; // two-byte integer: 1 byte = 20
5582+ offset = Util .setShort (buffer , offset , (short ) 0x0200 ); // uvModality "none": 2 bytes = 22
55455583
5546- buffer [offset ++] = 0x14 ; // map key: remainingDiscoverableCredentials
5547- short availableMem = JCSystem .getAvailableMemory (JCSystem .MEMORY_TYPE_PERSISTENT );
5548- short approximateKeyCount = (short )(availableMem / 128 );
5549- offset = encodeIntTo (buffer , offset , (byte )(approximateKeyCount > 100 ? 100 : approximateKeyCount ));
5584+ buffer [offset ++] = 0x14 ; // map key: remainingDiscoverableCredentials: 1 byte = 23
5585+ offset = encodeIntTo (buffer , offset , (byte )(approximateKeyCount > 100 ? 100 : approximateKeyCount )); // variable
55505586
55515587 if (longResponse ) {
5552- sendNoCopy (apdu , offset );
5588+ if (partiallySent ) {
5589+ bufferManager .clear ();
5590+ apdu .sendBytes ((short ) 0 , offset );
5591+ } else {
5592+ sendNoCopy (apdu , offset );
5593+ }
55535594 } else {
55545595 sendNoCopy (apdu , amountInApduBuf );
55555596 setupChainedResponse ((short ) 0 , offset );
0 commit comments