Skip to content

Commit 77c13c8

Browse files
committed
Repeated sendBytes in E-APDU case
1 parent 2036f12 commit 77c13c8

File tree

1 file changed

+78
-37
lines changed

1 file changed

+78
-37
lines changed

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

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)