Skip to content

Commit

Permalink
Reader: Show transaction times.
Browse files Browse the repository at this point in the history
This shows how a transaction takes, from the reader's perspective. We
show both the total and also broken down in various phases.

Test: Manually tested.
Test: All unit tests pass.

Signed-off-by: David Zeuthen <[email protected]>
  • Loading branch information
davidz25 committed Dec 11, 2023
1 parent de7d101 commit 7ba185b
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import androidx.navigation.fragment.findNavController
import com.android.identity.internal.Util
import com.android.identity.mdoc.response.DeviceResponseParser
import com.android.identity.securearea.SecureArea
import com.android.identity.securearea.SecureArea.EcCurve
import com.android.mdl.appreader.R
import com.android.mdl.appreader.databinding.FragmentShowDocumentBinding
import com.android.mdl.appreader.issuerauth.SimpleIssuerTrustStore
Expand Down Expand Up @@ -195,17 +194,28 @@ class ShowDocumentFragment : Fragment() {
}
}

sb.append("Number of documents returned: <b>${documents.size}</b><br>")
sb.append("Address: <b>" + transferManager.mdocConnectionMethod + "</b><br>")
// Get primary color from theme to use in the HTML formatted document.
val primaryColor = String.format(
"#%06X",
0xFFFFFF and requireContext().theme.attr(R.attr.colorPrimary).data
)

val totalDuration = transferManager.getTapToEngagementDurationMillis() +
transferManager.getEngagementToRequestDurationMillis() +
transferManager.getRequestToResponseDurationMillis()
sb.append("Tap to Engagement Received: ${transferManager.getTapToEngagementDurationMillis()} ms<br>")
sb.append("Engagement Received to Request Sent: ${transferManager.getEngagementToRequestDurationMillis()} ms<br>")
sb.append("Request Sent to Response Received: ${transferManager.getRequestToResponseDurationMillis()} ms<br>")
sb.append("<b>Total transaction time: <font color=\"$primaryColor\">$totalDuration ms</font></b><br>")
sb.append("<br>")

sb.append("Engagement Method: <b>" + transferManager.getEngagementMethod() + "</b><br>")
sb.append("Device Retrieval Method: <b>" + transferManager.mdocConnectionMethod + "</b><br>")
sb.append("Session encryption curve: <b>" + curveNameFor(transferManager.getMdocSessionEncryptionCurve()) + "</b><br>")
sb.append("<br>")

for (doc in documents) {
// Get primary color from theme to use in the HTML formatted document.
val color = String.format(
"#%06X",
0xFFFFFF and requireContext().theme.attr(R.attr.colorPrimary).data
)
sb.append("<h3>Doctype: <font color=\"$color\">${doc.docType}</font></h3>")
sb.append("<h3>Doctype: <font color=\"$primaryColor\">${doc.docType}</font></h3>")
val certPath =
simpleIssuerTrustStore.createCertificationTrustPath(doc.issuerCertificateChain.toList())
val isDSTrusted = simpleIssuerTrustStore.validateCertificationTrustPath(certPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,27 @@ class TransferManager private constructor(private val context: Context) {
fun getMdocSessionEncryptionCurve(): Int {
return Util.getCurve(verification!!.eReaderKeyPair.public)
}

fun getTapToEngagementDurationMillis(): Long {
return verification?.tapToEngagementDurationMillis ?: 0
}

fun getEngagementToRequestDurationMillis(): Long {
return verification?.engagementToRequestDurationMillis ?: 0
}

fun getRequestToResponseDurationMillis(): Long {
return verification?.requestToResponseDurationMillis ?: 0
}

fun getEngagementMethod(): String {
when (verification?.engagementMethod) {
VerificationHelper.ENGAGEMENT_METHOD_QR_CODE -> return "QR Code"
VerificationHelper.ENGAGEMENT_METHOD_NFC_STATIC_HANDOVER -> return "NFC Static Handover"
VerificationHelper.ENGAGEMENT_METHOD_NFC_NEGOTIATED_HANDOVER -> return "NFC Negotiated Handover"
VerificationHelper.ENGAGEMENT_METHOD_REVERSE -> return "Reverse"
}
return "N/A"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
import android.util.Base64;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;

import com.android.identity.securearea.SecureArea;
import com.android.identity.mdoc.sessionencryption.SessionEncryption;
import com.android.identity.android.mdoc.transport.DataTransport;
import com.android.identity.android.mdoc.transport.DataTransportBle;
Expand All @@ -44,13 +44,16 @@
import com.android.identity.mdoc.engagement.EngagementParser;
import com.android.identity.mdoc.request.DeviceRequestGenerator;
import com.android.identity.mdoc.response.DeviceResponseParser;
import com.android.identity.securearea.SecureArea;
import com.android.identity.util.Constants;
import com.android.identity.util.Logger;
import com.android.identity.internal.Util;
import com.android.identity.util.Timestamp;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -80,6 +83,24 @@
public class VerificationHelper {

private static final String TAG = "VerificationHelper";

public static final int ENGAGEMENT_METHOD_NOT_ENGAGED = 0;
public static final int ENGAGEMENT_METHOD_QR_CODE = 1;
public static final int ENGAGEMENT_METHOD_NFC_STATIC_HANDOVER = 2;
public static final int ENGAGEMENT_METHOD_NFC_NEGOTIATED_HANDOVER = 3;
public static final int ENGAGEMENT_METHOD_REVERSE = 4;

@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = false,
value = {
ENGAGEMENT_METHOD_NOT_ENGAGED,
ENGAGEMENT_METHOD_QR_CODE,
ENGAGEMENT_METHOD_NFC_STATIC_HANDOVER,
ENGAGEMENT_METHOD_NFC_NEGOTIATED_HANDOVER,
ENGAGEMENT_METHOD_REVERSE
})
@interface EngagementMethod {}

private Context mContext;
DataTransport mDataTransport;
Listener mListener;
Expand All @@ -103,6 +124,12 @@ public class VerificationHelper {
private List<ConnectionMethod> mConnectionMethodsForReaderEngagement;
private EngagementGenerator mReaderEngagementGenerator;
private byte[] mReaderEngagement;
private long mTimestampNfcTap;
private long mTimestampEngagementReceived;
private long mTimestampRequestSent;
private long mTimestampResponseReceived;
private @EngagementMethod int mEngagementMethod;


VerificationHelper() {
}
Expand Down Expand Up @@ -255,6 +282,8 @@ void reverseEngagementPeerHasConnected(@NonNull DataTransport transport) {
nfcProcessOnTagDiscovered(@NonNull Tag tag) {
Logger.d(TAG, "Tag discovered!");

mTimestampNfcTap = Timestamp.now().toEpochMilli();

// Find IsoDep since we're skipping NDEF checks and doing everything ourselves via APDUs
for (String tech : tag.getTechList()) {
if (tech.equals(IsoDep.class.getName())) {
Expand Down Expand Up @@ -331,7 +360,7 @@ public void setDeviceEngagementFromQrCode(@NonNull String qrDeviceEngagement) {
Logger.dCbor(TAG, "Device Engagement from QR code", encodedDeviceEngagement);

DataItem handover = SimpleValue.NULL;
setDeviceEngagement(encodedDeviceEngagement, handover);
setDeviceEngagement(encodedDeviceEngagement, handover, ENGAGEMENT_METHOD_QR_CODE);

EngagementParser engagementParser = new EngagementParser(encodedDeviceEngagement);
EngagementParser.Engagement engagement = engagementParser.parse();
Expand Down Expand Up @@ -612,7 +641,7 @@ public void run() {
.add(SimpleValue.NULL) // Handover Request message
.end()
.build().get(0);
setDeviceEngagement(hs.encodedDeviceEngagement, readerHandover);
setDeviceEngagement(hs.encodedDeviceEngagement, readerHandover, ENGAGEMENT_METHOD_NFC_STATIC_HANDOVER);
reportDeviceEngagementReceived(hs.connectionMethods);
return;
}
Expand Down Expand Up @@ -705,7 +734,7 @@ public void run() {
.add(hrMessage) // Handover Request message
.end()
.build().get(0);
setDeviceEngagement(encodedDeviceEngagement, handover);
setDeviceEngagement(encodedDeviceEngagement, handover, ENGAGEMENT_METHOD_NFC_NEGOTIATED_HANDOVER);

reportDeviceEngagementReceived(parsedCms);

Expand All @@ -717,11 +746,16 @@ public void run() {
transceiverThread.start();
}

private void setDeviceEngagement(@NonNull byte[] deviceEngagement, @NonNull DataItem handover) {
private void setDeviceEngagement(
@NonNull byte[] deviceEngagement,
@NonNull DataItem handover,
@EngagementMethod int engagementMethod) {
if (mDeviceEngagement != null) {
throw new IllegalStateException("Device Engagement already set");
}
mDeviceEngagement = deviceEngagement;
mEngagementMethod = engagementMethod;
mTimestampEngagementReceived = Timestamp.now().toEpochMilli();

EngagementParser engagementParser = new EngagementParser(deviceEngagement);
EngagementParser.Engagement engagement = engagementParser.parse();
Expand Down Expand Up @@ -871,7 +905,7 @@ private void handleReverseEngagementMessageData(@NonNull byte[] data) {
// is available and neither QR or NFC is used.
handover = Util.cborBuildTaggedByteString(mReaderEngagement);

setDeviceEngagement(encodedDeviceEngagement, handover);
setDeviceEngagement(encodedDeviceEngagement, handover, ENGAGEMENT_METHOD_REVERSE);

// Tell the application it can start sending requests...
reportDeviceConnected();
Expand Down Expand Up @@ -911,6 +945,7 @@ private void handleOnMessageReceived() {
//
if (decryptedMessage.getData() != null) {
Logger.dCbor(TAG, "DeviceResponse received", decryptedMessage.getData());
mTimestampResponseReceived = Timestamp.now().toEpochMilli();
reportResponseReceived(decryptedMessage.getData());
} else {
// No data, so status must be set...
Expand Down Expand Up @@ -1080,6 +1115,7 @@ public void sendRequest(@NonNull byte[] deviceRequestBytes) {
deviceRequestBytes, OptionalLong.empty());
Logger.dCbor(TAG, "SessionData to send", message);
mDataTransport.sendMessage(message);
mTimestampRequestSent = Timestamp.now().toEpochMilli();
}

/**
Expand Down Expand Up @@ -1165,6 +1201,40 @@ public void setSendSessionTerminationMessage(
mSendSessionTerminationMessage = sendSessionTerminationMessage;
}

/**
* Gets the amount of time from first NFC interaction until Engagement has been received.
*
* @return The duration, in milliseconds or 0 if NFC engagement isn't in use.
*/
public long getTapToEngagementDurationMillis() {
if (mTimestampNfcTap == 0) {
return 0;
}
return mTimestampEngagementReceived - mTimestampNfcTap;
}

/**
* Gets the amount of time from when Engagement has been received until the request was sent.
*
* @return The duration, in milliseconds.
*/
public long getEngagementToRequestDurationMillis() {
return mTimestampRequestSent - mTimestampEngagementReceived;
}

/**
* Gets the the amount of time from when the request was sent until the response was received.
*
* @return The duration, in milliseconds.
*/
public long getRequestToResponseDurationMillis() {
return mTimestampResponseReceived - mTimestampRequestSent;
}

public @EngagementMethod int getEngagementMethod() {
return mEngagementMethod;
}

/**
* Interface for listening to messages from the remote mdoc device.
*/
Expand Down

0 comments on commit 7ba185b

Please sign in to comment.