Format

EVO Wallet currently supports Verifiable Presentations, i.e. documents, in mdoc format, according to ISO 18013-5:2022. The standard defines them as Concise Binary Object Representation (CBOR) structures.

This section contains definitions in Concise Data Definition Language (CDDL) described in RFC 8610, a language used to define CBOR data structures. In CDDL, bstr refers to Byte String, defined as major type 2 and tstr refers to Text String, defined as major type 3 (encoded as UTF-8).

DeviceResponse, i.e. device retrieval mdoc response, shall be encoded and formatted as follows:

DeviceResponse = {
  "version": tstr,                      ; Version DeviceResponse, shall be "1.0"
  ? "documents": [+Document],           ; Returned documents
  ? "documentErrors": [+DocumentError]  ; error codes for unreturned documents
  "status": uint                        ; Status code, set to 0 when OK
}

Document = {
  "docType": tstr                       ; Document type returned
  "issuerSigned": IssuerSigned,         ; Returned data elements signed by the issuer
  "deviceSigned": DeviceSigned          ; Returned data elements signed by the wallet
  ? "errors": Errors
}

DocumentError = {
  DocType => ErrorCode                  ; Error codes for unreturned documents
}

IssuerSigned = {
  ? "nameSpaces": IssuerNameSpaces,     ; Returned data elemenents
  "issuerAuth": IssuerAuth              ; Contains the mobile security object (MSO)
                                        ; for issuer data authentication
}

IssuerNameSpaces = {
  + NameSpace => [+IssuerSignedItemBytes]     ; Data elements for each namespace
}

IssuerSignedItemBytes = #6.24(bstr .cbor IssuerSignedItem)

IssuerSignedItem = {
  "digestID": DigestID,                       ; Digest ID for issuer authentication
  "random": bstr,                             ; Random value for issuer authentication
  "elementIdentifier": DataElementIdentifier, ; Data element identifier
  "elementValue": DataElementValue            ; Data element value
}

IssuerAuth = Cose_Sign1             ; Untagged COSE_Sign1 signature of embedded
                                    ; MobileSecurityObjectBytes with Issuer certificate
                                    ; chain as COSE_X509 in x5chain unprotected header
                                    ; according to RFC 9360

MobileSecurityObjectBytes = #6.24(bstr .cbor MobileSecurityObject)

MobileSecurityObject = {
  "version": tstr,                  ; Version of MobileSecurityObject, shall be "1.0"
  "digestAlgorithm": tstr           ; Message digest algorithm used
  "valueDigests": ValueDigests,     ; Digests of all data elements per namespace
  "deviceKeyInfo": DeviceKeyInfo,   ; Info about device key
  "docType": DocType,               ; Document type
  "validityInfo": ValidityInfo      ; Info about document validity
  ? "status": StatusInfo            ; Info about document status
}

ValueDigests = {
  + NameSpace => DigestIDs          ; Digest IDs for each namespace
}

DigestIDs = {
  + DigestID => Digest              ; Digest value corresponding to DigestID
}

DeviceKeyInfo = {
  "deviceKey": DeviceKey,                   ; Device public key
  ? "keyAuthorizations": KeyAuthorizations, ; Device key usage authorizations
  ? "keyInfo": KeyInfo                      ; Device key information
}

DeviceKey = COSE_Key                        ; Untagged COSE_Key (RFC 8152)

KeyAuthorizations = {
  ? "nameSpaces": AuthorizedNameSpaces      ; Namespaces authorized for DeviceKey
  ? "dataElements": AuthorizedDataElements  ; Data elements authorized for DeviceKey
}

AuthorizedNameSpaces = [
  + NameSpace
]

AuthorizedDataElements = {
  + NameSpace => DataElementsArray
}

DataElementsArray = [
 + DataElementIdentifier
]

KeyInfo = {
 * int => any                   ; Positive integers are RFU, negative integers may
                                ; be for proprietary use
}

ValidityInfo = {
  "signed": tdate,
  "validFrom": tdate,
  "validUntil": tdate,
  ? "expectedUpdate": tdate
}

StatusInfo = {
  "status_list": StatusListInfo ; Reference to status CWT
}

StatusListInfo = {
  "idx": uint                   ; The index to check for status information
  "uri": tstr                   ; Status list token URI
}

DeviceSigned = {
  "nameSpaces": DeviceNameSpacesBytes,  ; Returned data elements
  "deviceAuth": DeviceAuth              ; Device authentication for mdoc
                                        ; authentication (i.e. presentation)
}

DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)

DeviceNameSpaces = {
  * NameSpace => DeviceSignedItems      ; Returned data elements for each namespace
}

DeviceSignedItems = {
  + DataElementIdentifier => DataElementValue ; Returned data element 
                                              ; identifier and value
}

DeviceAuth = {
  "deviceSignature": DeviceSignature          ; Signature for mdoc authentication
}

DeviceSignature = COSE_Sign1                  ; Untagged COSE_Sign1 signature of 
                                              ; detached DeviceAuthenticationBytes

DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)

DeviceAuthentication = [
  "DeviceAuthentication",
  SessionTranscript,
  DocType,
  DeviceNameSpacesBytes
]

SessionTranscript = [
  null,                     ; DeviceEngagementBytes set to null
  null,                     ; EReaderKeyBytes set to null
  OpenID4VPHandover         ; OpenID4VP-specific handover structure
]

OpenID4VPHandover = [
  "OpenID4VPHandover",       ; A fixed identifier for this handover type
  OpenID4VPHandoverInfoHash  ; A cryptographic hash of OpenID4VPHandoverInfo
]

OpenID4VPHandoverInfoHash = bstr ; SHA-256 hash of OpenID4VPHandoverInfoBytes

OpenID4VPHandoverInfoBytes = bstr .cbor OpenID4VPHandoverInfo

OpenID4VPHandoverInfo = [
  clientId: tstr,           ; client_id request parameter, including prefix
  nonce: tstr,              ; nonce request parameter
  jwkThumbprint: bstr,      ; JWK SHA-256 Thumbprint (RFC 7638) of the 
                            ; Verifier's public key used for encryption
  responseUri: tstr         ; response_uri request parameter
]

Errors = {
  + NameSpace => ErrorItems ; Error codes for each namespace
}

ErrorItems = {
  + DataElementIdentifier => ErrorCode  ; Error code per data element
}

DocType = tstr                          ; Document type

NameSpace = tstr                        ; Namespace name

DateElementIdentifier = tstr            ; Data element identifier (name)

DataElementValue = any                  ; Data element value

DigestID = uint                         ; Digest identifier used in IssuerSignedItem

Digest = bstr                           ; Hash value

ErrorCode = int                         ; Error code