Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reader: Show transaction times. #432

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading