This project is divided into two main libraries.
And one app to test these libraries.
Both libraries have one logger called ProximityLogger and CborLogger which can be used safely as in app MainActivity:
ProximityLogger.enabled = BuildConfig.DEBUG
CborLogger.enabled = BuildConfig.DEBUG
Public classes are:
- COSEManager
- MDoc
COSEManager is intended to be used to sign any bytes[] using COSE and also verify COSE signature.
Examples:
Sign data:
val data = //your byte array data
when (val result = coseManager.signWithCOSE(
data = data,
alias = "pagoPA"
)) {
is SignWithCOSEResult.Failure -> failureAppDialog(result.msg)
is SignWithCOSEResult.Success -> {
// handle result
signature = result.signature//bytes[]
publicKey = result.publicKey//bytes[]
}
}
Verify Sign1Message:
// it returns a Boolean
coseManager.verifySign1(
dataSigned = what,
publicKey = pubKey
)
In app you can find these examples into SignAndVerifyViewViewModel class.
MDoc:
The MDoc
class is a polymorphic class in Kotlin that returns an object of type ModelMDoc
. The ModelMDoc
is a
parser for a CBOR format, which includes a method to convert it to JSON.
The MDoc
class has three constructors:
-
Primary Constructor: This is private and is used internally by the other constructors.
private constructor(source: Any, isByteArray: Boolean = false)
-
String Constructor: This takes a
Base64String
source and setsisByteArray
tofalse
.constructor(source: String) : this(source, false)
-
ByteArray Constructor: This takes a
bytes[]
source and setsisByteArray
totrue
.constructor(source: ByteArray) : this(source, true)
Public classes are:
- QrEngagement
- ResponseGenerator
and a data class:
/**
* BLE Retrieval Method
* @property peripheralServerMode set if the peripheral server mode is enabled
* @property centralClientMode set if the central client mode is enabled
* @property clearBleCache set if the BLE cache should be cleared
*/
data class BleRetrievalMethod(
val peripheralServerMode: Boolean,
val centralClientMode: Boolean,
val clearBleCache: Boolean
) : DeviceRetrievalMethod
QrEngagement:
companion object {
/**
* Create an instance and configures the QR engagement.
* First of all you must call [configure] to build QrEngagementHelper.
* To accept just some certificates use [withReaderTrustStore] method.
* To create a QrCode use [getQrCodeString] method.
* To observe all events call [withListener] method.
* To close the connection call [close] method.
*/
fun build(context: Context, retrievalMethods: List<DeviceRetrievalMethod>): QrEngagement {
return QrEngagement(context).apply {
this.retrievalMethods = retrievalMethods
qrEngagementBuilder = QrEngagementHelper.Builder(
context,
eDevicePrivateKey.publicKey,
retrievalMethods.transportOptions,
qrEngagementListener,
context.mainExecutor()
).setConnectionMethods(retrievalMethods.connectionMethods)
}
}
}
This is thew way this class is intended to be instantiated. Examples can be found into MasterViewViewModel class.
Methods:
-
configure: builds QrEngagementHelper by com.android.identity package and returns QrEngagement instance created via QrEngagement.build static method.
fun configure() = apply { qrEngagement = qrEngagementBuilder.build() }
-
withReaderTrustStore: Method to inject certificates to be verified sent by mdoc verifier app.
/** * Use this if you have certificates into your **Raw Resource** folder. * *You have still other two methods with [List] of [ByteArray] for raw certificates and [List] of [String] for pem* * @param certificates a [List] of [Int] representing your raw resource * @return [QrEngagement] */ fun withReaderTrustStore(certificates: List<Int>) = apply { certificates.setReaderTrustStore() } /** * Use this if you have certificates **As [ByteArray]**. * *You have still other two methods with [List] of [Int] for raw resources and [List] of [String] for pem* * @param certificates a [List] of [ByteArray] representing your raw certificates * @return [QrEngagement] */ @JvmName("withReaderTrustStore1") fun withReaderTrustStore(certificates: List<ByteArray>) = apply { certificates.setReaderTrustStore() } /** * Use this if you have certificates **As [String]**. * *You have still other two methods with [List] of [Int] for raw resources and [List] of [ByteArray] for raw certificates* * @param certificates a [List] of [String] representing your pem certificates * @return [QrEngagement] */ @JvmName("withReaderTrustStore2") fun withReaderTrustStore(certificates: List<String>) = apply { certificates.setReaderTrustStore() }
-
getQrCodeString: Gives back QR code string for engagement
fun getQrCodeString() = apply { if (!checkQrEngagementInit()) return "" return qrEngagement.deviceEngagementUriEncoded }
-
withListener: Starts the listener for qrCodeEngagement
fun withListener(callback: QrEngagementListener) = apply { this.listener = callback }
-
close: Closes the connection with the mdoc verifier
fun close() { if (!checkQrEngagementInit()) return try { if (deviceRetrievalHelper != null) deviceRetrievalHelper!!.disconnect() qrEngagement.close() } catch (exception: RuntimeException) { ProximityLogger.e(this.javaClass.name, "Error closing QR engagement $exception") } }
The listener:
interface QrEngagementListener {
fun onConnecting()
fun onDeviceRetrievalHelperReady(deviceRetrievalHelper: DeviceRetrievalHelperWrapper)
fun onCommunicationError(msg: String)
fun onNewDeviceRequest(request: String?, sessionsTranscript: ByteArray)
fun onDeviceDisconnected(transportSpecificTermination: Boolean)
}
ResponseGenerator:
interface Response {
/**@param [response] [ByteArray] generated for response*/
fun onResponseGenerated(response: ByteArray)
/**@param [message] [String] for error reached*/
fun onError(message: String)
}
/**
* It creates a mdoc response in ByteArray format respect documents requested and disclosed
* @return[Response.onResponseGenerated] if ByteArray is created without Exceptions, else
* [Response.onError] if disclosedDocumentsArray is Empty with "no doc found" message or if an
* [Exception] was reached with [Throwable.message].
*/
@JvmName("createResponseWithCallback")
fun createResponse(
documents: Array<DocRequested>,
fieldRequestedAndAccepted: String,
response: Response
) {
val (responseToSend, message) = this.createResponse(
documents, fieldRequestedAndAccepted
)
responseToSend?.let {
response.onResponseGenerated(it)
} ?: run {
response.onError(message)
ProximityLogger.e(
"Sending resp",
"found doc but fail to generate raw response: $message"
)
}
}
where DocRequested is:
@Parcelize
data class DocRequested(
val content: String,
val alias: String
) : Parcelable
See in app MasterViewViewModel.shareInfo method to understand how to retrieve documents from JSON request and correctly send to response.