Aceptación en persona de credenciales digitales (sin conexión)

En esta guía, se explica cómo las partes de confianza (RP) y los desarrolladores de lectores pueden implementar la verificación en persona (sin conexión) de las credenciales digitales presentadas desde la Billetera de Google de acuerdo con el estándar internacional ISO/IEC 18013-5.

Las credenciales digitales de la Billetera de Google se pueden verificar de forma segura en entornos físicos (como terminales de punto de venta, lugares de eventos, puertas de transporte público, lectores de autoridades de orden público y apps de lectura para dispositivos móviles) sin necesidad de una conexión a Internet activa en el momento de la presentación.

Descripción general de la presentación sin conexión (ISO/IEC 18013-5)

El estándar ISO/IEC 18013-5 define un protocolo estandarizado e interoperable para la presentación sin conexión entre un titular (el dispositivo móvil del usuario que ejecuta la Billetera de Google) y un lector o verificador (una terminal física o una app complementaria para dispositivos móviles).

El flujo de presentación se produce en distintas fases:

  1. Interacción con el dispositivo: El lector y la billetera establecen el contacto inicial a través de un toque NFC (transferencia estática o negociada) o un escaneo de código QR. Durante esta fase, se intercambian los metadatos de participación del dispositivo y la clave pública efímera del lector (EReaderKey).
  2. Conexión de transporte de datos: Se negocia un canal seguro y encriptado de Bluetooth de bajo consumo (BLE) (con el lector actuando en modo de cliente central o servidor periférico).
  3. Solicitud del dispositivo: El lector transmite un DeviceRequest codificado en CBOR que especifica el tipo de documento solicitado (como org.iso.18013.5.1.mDL) y los espacios de nombres y elementos de datos específicos solicitados.
  4. Consentimiento del usuario y autenticación del dispositivo: La Billetera de Google le solicita al usuario que revise los elementos de datos solicitados y confirme el uso compartido con la autenticación biométrica o el bloqueo de pantalla.
  5. Respuesta del dispositivo y verificación criptográfica: La billetera devuelve un DeviceResponse codificado en CBOR que contiene el objeto de seguridad móvil (MSO) firmado y los elementos de datos firmados por el dispositivo. El lector verifica las firmas criptográficas con los certificados raíz de confianza.

El SDK de código abierto de Multipaz

Para implementar una aplicación de lector o verificador, Google recomienda usar Multipaz, un SDK de código abierto de Kotlin Multiplatform (KMP) desarrollado originalmente por Google y aportado a la OpenWallet Foundation (OWF).

Multipaz proporciona una implementación lista para producción de los protocolos de lector y billetera ISO/IEC 18013-5, canalizaciones de verificación criptográfica, codificación y decodificación CBOR, y esquemas de tipos de documentos extensibles.

Cómo integrar Multipaz en tu app de lector

En los siguientes pasos, se muestra cómo integrar el SDK de Multipaz en una aplicación de lector para Android.

Paso 1: Agrega dependencias

Las bibliotecas de Multipaz se publican en Maven Central. Agrega los módulos necesarios al archivo build.gradle.kts de tu aplicación:

// build.gradle.kts
dependencies {
    // Core Multipaz library (protocol engine, CBOR, crypto)
    implementation("org.multipaz:multipaz:0.100.0")

    // Android-specific platform bindings (NFC, BLE, Keystore)
    implementation("org.multipaz:multipaz-android:0.100.0")

    // Standardized document types (mDL, EU PID, etc.)
    implementation("org.multipaz:multipaz-doctypes:0.100.0")
}

Paso 2: Configura los permisos de Android

La verificación presencial requiere permisos de hardware para la interacción con NFC, el escaneo con la cámara (para la interacción con QR) y el transporte de datos con Bluetooth de bajo consumo. Agrega los siguientes permisos a tu AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- NFC Engagement -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="false" />

    <!-- Camera for QR Code Engagement -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />

    <!-- Bluetooth Low Energy Transport (Android 12+) -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- Legacy Bluetooth Permissions for Android 11 and lower -->
    <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
                     android:maxSdkVersion="30" />
</manifest>

Paso 3: Inicializa el motor de verificación

Usa las abstracciones de conexión VerificationHelper o Reader de Multipaz para controlar los eventos de participación y administrar el ciclo de vida de la comunicación de BLE:

import org.multipaz.verification.VerificationHelper
import org.multipaz.cbor.Cbor
import org.multipaz.crypto.Crypto

class ReaderManager(private val context: android.content.Context) {

    private var verificationHelper: VerificationHelper? = null

    fun startListeningForEngagement() {
        verificationHelper = VerificationHelper.Builder(
            context = context,
            listener = object : VerificationHelper.Listener {
                override fun onDeviceConnected() {
                    // BLE channel established, send request
                    sendDeviceRequest()
                }

                override fun onResponseReceived(deviceResponseBytes: ByteArray) {
                    // Process and verify the received credential payload
                    handleDeviceResponse(deviceResponseBytes)
                }

                override fun onError(error: Throwable) {
                    // Handle transport or protocol errors
                }

                override fun onDeviceDisconnected(transportTransportSpecificTermination: Boolean) {
                    // Connection closed
                }
            }
        ).build()
    }
}

Paso 4: Crea la DeviceRequest

Especifica el tipo de documento y los elementos de datos individuales que tu aplicación necesita verificar. Aplica siempre el principio de divulgación mínima (p.ej., solicita solo age_over_21 en lugar de birth_date completo cuando verifiques la edad):

fun sendDeviceRequest() {
    // Specify the DocType and requested elements
    val docType = "org.iso.18013.5.1.mDL"
    val namespace = "org.iso.18013.5.1"

    // Map of requested elements: elementName -> intentToRetain
    val requestedElements = mapOf(
        "family_name" to false,
        "given_name" to false,
        "age_over_21" to false,
        "portrait" to false,
        "driving_privileges" to false
    )

    // Build ISO/IEC 18013-5 DeviceRequest structure
    val deviceRequestBytes = verificationHelper?.buildDeviceRequest(
        docType = docType,
        itemsToRequest = mapOf(namespace to requestedElements)
    )

    if (deviceRequestBytes != null) {
        verificationHelper?.sendDeviceRequest(deviceRequestBytes)
    }
}

Compatibilidad con tipos de documentos adicionales (DocTypes)

Multipaz admite credenciales estandarizadas listas para usar y proporciona una arquitectura extensible para solicitar tipos de documentos personalizados o específicos del dominio.

1. Tipos de documentos estándar integrados

La biblioteca multipaz-doctypes proporciona modelos de esquema predefinidos para las credenciales estándar:

Identificador del tipo de documento Estándar / Alcance Espacio de nombres típico Elementos comunes
org.iso.18013.5.1.mDL Licencia de conducir digital ISO/IEC 18013-5 org.iso.18013.5.1 family_name, given_name, birth_date, issue_date, expiry_date, issuing_authority, document_number, portrait, driving_privileges, age_over_18, age_over_21
eu.europa.ec.eudi.pid.1 Datos de identificación personal de la Billetera de Identidad Digital de la UE (EUDIW) eu.europa.ec.eudi.pid.1 family_name, first_name, birth_date, nationality, issuing_country, issuing_authority, personal_administrative_number
com.google.wallet.idcard.1 Pase de identificación de la Billetera de Google / IDs de prueba com.google.wallet.idcard.1 given_name, family_name, birth_date, document_number y portrait

2. Cómo solicitar tipos de documentos personalizados

Para solicitar tipos de documentos personalizados, define la cadena docType de destino y las asignaciones de espacio de nombres correspondientes cuando construyas el DeviceRequest:

// Example: Requesting a custom event ticket credential
val customDocType = "com.example.events.ticket"
val customNamespace = "com.example.events.ticket.1"

val customRequestedItems = mapOf(
    "ticket_id" to false,
    "event_name" to false,
    "seat_section" to false,
    "vip_access" to false
)

val multiDocRequestBytes = verificationHelper?.buildMultiDocDeviceRequest(
    documents = listOf(
        DocumentRequest(
            docType = customDocType,
            namespaces = mapOf(customNamespace to customRequestedItems)
        )
    )
)

Verificación criptográfica y administración de confianza

Recibir una carga útil de respuesta es solo el primer paso. Los lectores deben realizar una verificación criptográfica de cuatro pasos para validar la autenticidad y la integridad de la credencial presentada.

Paso de verificación Objetivo de verificación
1. Autenticación de la entidad emisora Verifica IssuerAuth (COSE_Sign1) con los certificados raíz de la IACA de confianza
2. Verificación del período de validez Asegúrate de que validFrom ≤ hora actual ≤ validUntil
3. Verificación de la integridad de los datos Calcula los resúmenes SHA-256 de los elementos mostrados y compáralos con el MSO ValueDigests
4. Autenticación en dispositivos Verifica la firma o MAC de DeviceSigned con el DeviceKey vinculado al SessionTranscript

1. La canalización de verificación de 4 pasos

  1. Autenticación del emisor (IssuerAuth):
    • La entidad emisora firma el objeto de seguridad móvil (MSO) (carga útil de IssuerAuth).
    • El lector verifica la firma de COSE_Sign1 con el certificado de firma de documentos y se asegura de que el certificado se encadene a un certificado raíz de autoridad certificadora emisora (IACA) de confianza.
  2. Verificación de la ventana de validez:
    • El lector verifica las marcas de tiempo validityInfo.validFrom y validityInfo.validUntil en el MSO con el reloj actual del lector para asegurarse de que la credencial no haya vencido.
  3. Verificación de la integridad de los datos (ValueDigests):
    • Por cada IssuerSignedItem recibido, el lector calcula su resumen (p.ej., SHA-256) y verifica que coincida con la entrada de hash correspondiente en el diccionario ValueDigests del MSO.
  4. Autenticación de dispositivos (DeviceSigned):
    • El lector valida que el dispositivo que presenta la credencial tenga la clave privada correspondiente al DeviceKey publicado dentro del MSO firmado.
    • Esto se logra verificando el DeviceAuth (ya sea DeviceSignature o DeviceMac) sobre el SessionTranscript, vinculando la sesión a la clave efímera del lector y evitando ataques de repetición y de intermediario.

2. Administra los certificados raíz de confianza de la IACA

Los lectores de producción deben mantener un almacén de confianza local y seguro que contenga certificados raíz de IACA de confianza:

  • Certificados de IACA de producción: Descarga y configura certificados raíz de las autoridades emisoras oficiales. Consulta nuestra lista de entidades emisoras y certificados de IACA compatibles.
  • VICAL de la AAMVA: En las jurisdicciones de EE.UU., los sistemas de lectores pueden integrarse con el servicio de la lista de autoridades certificadoras de emisores verificados (VICAL) de la Asociación Estadounidense de Administradores de Vehículos Motorizados (AAMVA) para sincronizar automáticamente las anclas de confianza estatales.
  • Raíces de pruebas de la zona de pruebas: Cuando realices pruebas con credenciales de la zona de pruebas, asegúrate de que el lector confíe en la raíz de la IACA de la zona de pruebas de Google.
import org.multipaz.crypto.X509Cert

// Configure trusted IACA certificates in the trust store
val trustedCertificates = mutableListOf<X509Cert>()

// Add official state IACA certificates
trustedCertificates.add(X509Cert.fromPem(sampleStateIacaPem))

// Add Google Sandbox IACA root certificate for testing
trustedCertificates.add(X509Cert.fromPem(googleSandboxIacaPem))

val verifier = MultipazVerifier(trustStore = trustedCertificates)
val verificationResult = verifier.verify(deviceResponseBytes, sessionTranscript)

if (verificationResult.isIssuerAuthorized && verificationResult.isDeviceAuthenticated) {
    // Credential is valid and authentic
} else {
    // Reject presentation: cryptographic validation failed
}

Autenticación del lector (recomendado)

La autenticación del lector permite que la aplicación del lector demuestre criptográficamente su identidad a la Billetera de Google firmando la estructura ReaderAuthentication con un certificado de lector X.509 autorizado.

  • Por qué se recomienda: La autenticación del lector permite que tu app o terminal de lectura presente una identidad de confianza al usuario. Si bien es opcional para los atributos públicos básicos (p.ej., verificar age_over_21), se recomienda para las integraciones de lectores para aumentar la confianza de los usuarios y puede ser un requisito legal o de política cuando se solicitan atributos sensibles (como el número de seguridad social completo, la dirección residencial o las aprobaciones estatales específicas).
  • Cómo funciona: El lector incluye su cadena de certificados y firma la transcripción de la sesión. La Billetera de Google muestra la identidad verificada y el nombre de la organización del lector al usuario en la pantalla de consentimiento antes de que se divulguen los datos.

Herramientas de prueba y desarrollo

Para acelerar la integración, usa las siguientes herramientas para desarrolladores y las implementaciones de referencia:

  1. Apps de referencia de Multipaz:
    • Clona el repositorio de Multipaz y ejecuta la app de ejemplo de Android IdentityReader para probar los flujos de verificación física.
  2. Crea un ID de prueba en la Billetera de Google:
  3. Pruebas del verificador basado en la Web:
    • Usa verifier.multipaz.org para inspeccionar solicitudes de CBOR, explorar consultas de reclamaciones y probar presentaciones basadas en la Web de W3C / ISO 18013-7.

Solución de problemas y diagnóstico de campo

En la siguiente tabla, se enumeran los problemas habituales que se producen durante la verificación sin conexión y las soluciones recomendadas:

Problema o síntoma Causa raíz Resolución recomendada
Tiempo de espera de conexión BLE agotado / No se pudo establecer la conexión
  • Interferencia de RF en entornos de alta densidad
  • Incompatibilidad entre el modo periférico y el central en hardware de lector específico.
  • Tiempos de espera de análisis.
  • Asegúrate de que el lector admita los modos de cliente central y servidor periférico de BLE.
  • Ajusta la ventana y el intervalo de búsqueda de BLE para realizar una búsqueda intensiva durante la participación activa.
  • Verifica que la negociación del tamaño de la MTU se complete correctamente.
La interacción de pago sin contacto con NFC falla o se interrumpe El usuario aleja el dispositivo móvil de la antena del lector antes de que se transfiera por completo el registro de transferencia de BLE.
  • Proporciona comentarios visuales, de audio o táctiles inmediatos en la terminal en cuanto comience la interacción con NFC.
  • Indica a los usuarios que mantengan el teléfono quieto contra el objetivo NFC hasta que se establezca la conexión BLE.
UNTRUSTED_ISSUER / Falla en la cadena de certificados El certificado de firma de documentos no se encadena a ningún certificado de IACA de confianza en el almacén de confianza local del lector.
  • Verifica que el certificado raíz de la entidad emisora esté cargado en el almacén de confianza del lector.
  • Si realizas pruebas en la zona de pruebas, verifica que se cargue la raíz de la IACA de la zona de pruebas de Google.
  • Asegúrate de que las listas de certificados de IACA (p.ej., VICAL de la AAMVA) se actualicen periódicamente.
INVALID_VALIDITY_INFO / MSO vencido
  • El reloj del sistema del lector no está sincronizado.
  • La firma de MSO venció.
  • Asegúrate de que el dispositivo lector sincronice su hora del sistema con regularidad a través de NTP.
  • Solicita al usuario que abra la Billetera de Google mientras se conecta a Internet para actualizar los tokens de credenciales.
DEVICE_AUTHENTICATION_FAILED No coincide la transcripción de la sesión entre el lector y la billetera, o la firma efímera del dispositivo no es válida.
  • Asegúrate de que los bytes sin procesar exactos de DeviceEngagementBytes y EReaderKeyBytes se conserven en la estructura SessionTranscript sin volver a codificarlos.
Falla de permisos en Android 12 y versiones posteriores La aplicación intentó realizar un análisis o publicar anuncios a través de BLE sin permisos de tiempo de ejecución.
  • Verifica y solicita BLUETOOTH_SCAN, BLUETOOTH_CONNECT y BLUETOOTH_ADVERTISE en el entorno de ejecución antes de iniciar las sesiones del lector.

Lineamientos de UX y privacidad para lectores en persona

Cuando diseñes lectores físicos y apps complementarias, ten en cuenta lo siguiente:

  • Practica la divulgación selectiva en la IU: Muestra solo la decisión o el atributo mínimo requerido al operador (p.ej., muestra una marca de verificación verde destacada y "Edad mayor de 21 años verificada" en lugar de mostrar la fecha de nacimiento completa, la dirección y el número de licencia del usuario).
  • Indicadores claros de interacción física: Etiqueta claramente la zona de destino de NFC y muestra indicadores visuales (como animaciones o barras de progreso) que muestren cada etapa: Presionar / escanear → Conectando → Verificando → Completado.
  • Manejo de datos efímeros: No almacenes ni registres elementos de datos personales recibidos de la billetera, a menos que lo exija explícitamente la ley aplicable y se divulgue a través de intentToRetain = true.