- A partir del objetivo de Seattle Public Library y King County Library System de reducir las tarjetas plásticas, terminé manipulando directamente la estructura de JSON, imágenes y firmas de los pases de Wallet en iPhone
- Wallet solo admite QR code, PDF417, Aztec Code y Code 128, así que no se podía poner Codabar, usado por las tarjetas de biblioteca, como código de barras nativo; el código de barras real para escanear se resolvió con una imagen
- El pase debía firmarse con una clave confiable para Apple, pero en lugar de pagar una membresía de Apple Developer de 99 dólares al año, encontré y usé un certificado y una clave en una app de pases para iOS que podía generar pases sin conexión
- Combinando el layout
storeCard, logos para distintas resoluciones, el Codabarstrip.pngy un campo vacíoPKBarcodeFormatCode128, se muestra en pantalla un código de barras como imagen mientras se conserva el comportamiento de iOS de aumentar el brillo de la pantalla - El
.pkpassfinal se pudo crear generando el manifest, firmando conopenssl smimey empaquetando en ZIP; aunque aún no se probó en una biblioteca real, un escáner aparte sí leyó el código de barras desde la pantalla del teléfono
Objetivo: usar el código de barras de la biblioteca sin tarjeta plástica
- Seattle Public Library y King County Library System emiten tarjetas plásticas con código de barras impreso vinculadas a las cuentas de préstamo
- A 2023, la mayoría de las funciones de la biblioteca de Seattle son de autoservicio y normalmente se puede ingresar manualmente el código de barras de la biblioteca
- Aun así, mostrar el código de barras de inmediato es mucho más cómodo, así que el objetivo era no llevar en la billetera una tarjeta plástica que casi no se usa y ponerla en la app Wallet del iPhone
Los pases de Wallet están compuestos por JSON, imágenes y una firma
- Hoy la app Wallet maneja tarjetas de pago, identificaciones, llaves y más, pero cuando Passbook se lanzó en 2012 solo gestionaba “pases”
- Según la documentación de pases de Apple, un pase es un archivo ZIP autocontenido que puede distribuirse por correo o por la web, y que contiene JSON y PNG
- La composición básica de un archivo
.pkpasses simplepass.json: describe el contenido del pase que no son imágenesmanifest.json: lista de checksums SHA-1 de los demás archivossignature: firma S/MIME sobre el contenido demanifest.json- Archivos de imagen compatibles
La falta de soporte para Codabar fue la primera barrera
- Los pases de Wallet solo admiten cuatro tipos de códigos de barras
- QR code
- PDF417
- Aztec Code
- Code 128
- La tarjeta de biblioteca usaba el formato Codabar, ampliamente utilizado en bibliotecas
- Es posible que los escáneres de códigos de barras de la biblioteca admitan otros códigos de barras lineales, pero el único formato que podía asumirse que funcionaría con certeza en todas las sucursales era Codabar
- Al final, en lugar de usar la función nativa de códigos de barras de Wallet, había que crear una imagen Codabar escaneable e incluirla dentro del pase
El problema del certificado para firmar el pase
- Los pases de Wallet requieren una firma criptográfica y deben estar firmados con una clave confiable para una de las autoridades certificadoras de Apple
- Para casos como que un proveedor actualice automáticamente un pase por cambios de puerta o de asiento en un boleto de avión, la firma tiene cierto sentido
- Como desarrollador de Apple, se puede obtener una clave de firma de pases con relativa facilidad, pero no quería pagar 99 dólares al año por esta tarea
Por qué las apps existentes para generar pases no servían
- Había varias apps de diseño de pases creadas por personas que ya eran desarrolladoras de Apple, pero no ofrecían el nivel de control necesario
-
Control del tamaño del logo
- Apple permite una altura máxima de 50 device-independent pixels para el logo de la esquina superior izquierda del pase
- Para el logo cuadrado que va junto al texto a la derecha, una altura de unos 40px era lo más adecuado
- El desarrollador debe proporcionar
logo.png,logo@2x.pngylogo@3x.pngpor separado, pero las apps normalmente solo permiten elegir un único logo y no dan control de escala
-
Comportamiento del brillo de la pantalla
- Al abrir un pase con un código de barras compatible normal, la pantalla aumenta el brillo para mejorar el contraste ante el escáner
- Para que iOS aumentara el brillo sin mostrar un código de barras que en realidad no funcionara, había que manipular directamente
pass.json - Algunas apps descargables gratis limitaban la cantidad de pases que podían guardarse en Wallet y pedían un pago
Encontrar una clave de firma en una app de generación offline
- Descargué varias apps de generación de pases con inicio gratuito y verifiqué si podían crear pases también sin conexión
- Al probar con modo avión y Wi-Fi desactivado, al menos una app pudo crear pases offline
- No revelo el nombre específico de esa app para evitar que se revoque la clave
- El proceso fue más simple de lo esperado
- Como las apps de iOS pueden ejecutarse en una Mac con Apple silicon, descargué la app en la Mac
- Dentro del bundle de la app de iOS envuelta, busqué un archivo
.p12llamativo - Como los archivos PKCS#12 requieren una contraseña de importación, ejecuté
stringssobre el binario principal y busqué cadenas que parecieran contraseñas
- Al revisar la información del certificado con
openssl pkcs12, el issuer apareció como Apple Worldwide Developer Relations Certification Authority - También se necesita la cadena de certificados; el certificado intermedio podría estar dentro del bundle de la app y también puede descargarse desde la URL incluida en los campos de extensión X.509
Configuración del layout del pase
- Para el estilo del pase, hay que elegir uno entre
boarding pass,coupon,event ticket,genericystore card - Para incluir una imagen horizontal grande, se necesita un layout que admita imágenes
strip- Las opciones eran
coupon,event ticketystore card - Elegí store card porque era el formato más parecido a una tarjeta de biblioteca física
- Las opciones eran
pass.jsoncontiene los siguientes valorespassTypeIdentifierteamIdentifierformatVersionserialNumberorganizationNamelogoTextdescriptionstoreCardbackgroundColorforegroundColorsharingProhibited
passTypeIdentifieryteamIdentifierdeben coincidir respectivamente con los camposUIDyOUdel subject del certificado de Apple- Cada pase creado con el mismo
passTypeIdentifiernecesita unserialNumberúnico - Preparé las imágenes según su uso
icon.pnges obligatorio, pero no se muestra en el pase en sílogo.pnges el logo que se muestra en la esquina superior izquierda- Generé
logo.pngde 40×40,logo@2x.pngde 80×80 ylogo@3x.pngde 120×120 - Copié
logo@3x.pngcomoicon.png - El código de barras pregenerado se puso en
strip.png
Crear la imagen de código de barras Codabar
- iOS escala y recorta
strip.pngpara ajustarla al recuadro en la pantalla del dispositivo, así que no fue necesario crear tres variantes destrip.pngpara distintas resoluciones - Las dos tarjetas de biblioteca usan A y D como símbolos de inicio y fin
- Los símbolos de inicio y fin se pueden verificar más fácilmente con un escáner de códigos de barras; si no se tiene uno, se pueden comparar visualmente con la tabla de codificación Codabar de Wikipedia
- No había muchos generadores Codabar listos para usar en línea, pero el formato en sí era fácil de implementar directamente
- En el prototipo, generé SVG con la biblioteca Barcoders para Rust, ajusté el SVG y luego lo exporté a PNG
- El layout final se definió considerando la posibilidad de escaneo y la reducción de escala de iOS
- La altura del código de barras se fijó en unidades equivalentes al doble del número total de símbolos, incluyendo los de inicio y fin
- Por ejemplo, un número de código de barras de 13 dígitos tiene 15 símbolos, así que la altura se hizo de 30 unidades
- Antes del inicio y después del fin se dejó un quiet space de 15 unidades en cada lado
- Arriba y abajo del código de barras se dejó un padding de 50 unidades en cada lado
- Cada unidad se amplió a 8 píxeles para que iOS siempre redujera la imagen
- En el ejemplo de código de barras de 15 símbolos, la imagen final mide 1040 píxeles de alto y el código de barras en sí mide 240 píxeles de alto
- Escribí un script de shell de 69 líneas que genera un BMP Codabar con ese layout y lo convierte a PNG con
sips - Guardando la salida del script como
strip.png, queda lista la imagen del código de barras para incluirla en el pase
Mostrar el número del código de barras y el atajo para el brillo de pantalla
- El número del código de barras se muestra debajo del código usando
secondaryFieldskey:numberlabel:CARD NUMBERvalue: número de tarjeta
- iOS aumenta el brillo de la pantalla al seleccionar un pase con código de barras, para ayudar al escáner
- Con solo el Codabar insertado como imagen, iOS no considera que haya un código de barras
- Para conseguir ese comportamiento, se especificó un campo de código de barras vacío en el nivel superior de
pass.jsonmessagees una cadena vacíaformatesPKBarcodeFormatCode128messageEncodingesiso-8859-1
- Este método hizo que el iPhone aumentara el brillo de la pantalla como si el pase tuviera un código de barras, sin mostrar un código de barras en la parte inferior del pase
Firma y empaquetado
- Después de preparar todos los archivos, hay que generar
manifest.json manifest.jsones un objeto que usa nombres de archivo como claves y checksums SHA-1 como valores- Combiné
sha1sumyjqpara generar el manifest de los archivos PNG ypass.json - La firma del manifest se realiza con el comando
openssl smime- Certificado del firmante
- Private key
- Certificado intermedio Apple WWDR
- Entrada
manifest.json - Salida
signature
- Con la opción
-attimedeopenssl smime, se puede especificar el momento de firma deseado- El valor de la opción es un UNIX epoch
- Aunque el certificado recibido de Apple o encontrado haya caducado, se puede firmar con ese momento
- Por último, se agrupan los siguientes archivos en un ZIP para crear el
.pkpass- Imágenes PNG
pass.jsonmanifest.jsonsignature
Resultado de las pruebas y puntos pendientes
- macOS tiene una herramienta de vista previa de pases que permite comprobar si el pase es válido y si, en general, se ve correctamente
- Si no es válido, se pueden buscar errores en
Console.app - La herramienta de vista previa no es 100% precisa, pero tiene un botón para enviarlo al iPhone mediante iCloud
- El pase terminado aún no se probó en una biblioteca real
- Un escáner de códigos de barras aparte sí pudo leer el código de barras desde la pantalla del teléfono igual que desde la tarjeta plástica real
- Aunque se leía bien cuando el brillo de la pantalla estaba al máximo
- El brillo se configuró más alto que el que subía con el atajo del código de barras vacío
- Es bueno que la especificación de pases casi no haya cambiado en 10 años, pero es una lástima que se necesite una membresía de Apple Developer de 99 dólares al año para firmar un pase inofensivo hecho de PNG y JSON
- Algunas funciones de los pases sí pueden requerir firma, pero considero que lo hecho en esta tarea no debería necesitarla
- Si Apple agrega soporte para Codabar en Wallet, se podrían admitir tarjetas de biblioteca digitales sin auditar si todos los escáneres del sistema bibliotecario admiten Code 128
1 comentarios
Opiniones de Hacker News
La próxima estaría bueno que también cubran la tarjeta ORCA. Seattle es uno de los centros tecnológicos de EE. UU., pero no solo el metro/transporte público en sí es bastante malo, sino que además está tecnológicamente atrasado frente al transporte público de casi cualquier ciudad importante que haya usado.
Cualquier ciudad de Europa, CDMX o Denver están mucho más adelante que Seattle, y personalmente creo que Denver fue la mejor.
Una de las agencias que operan ORCA incluso había publicado una entrada de blog diciendo que iba a agregar soporte NFC a la app de Android, pero esa publicación ya desapareció y, años después, la función sigue sin existir.
Mientras uses la misma tarjeta, maneja sin fricciones cosas como transbordos y pases de varios días, y aplica automáticamente la tarifa correcta, por ejemplo dejando de cobrarte si haces más de cierta cantidad de viajes dentro de un período determinado.
La puerta de mi oficina se abre con una tarjeta NFC y busqué si era posible en iOS, pero según recuerdo Apple controla el hardware NFC con más rigidez que el PassKit común, así que con apps normales parece difícil.
Dijeron que el pago por toque llegaría a ORCA en 2023, así que si va a ser cierto, quedan unos dos meses.
Como Google publicó que pronto soportará ORCA en Google Wallet, soy optimista.
Si eres usuario de Android y no te gusta que Google Wallet no permita agregar cualquier código de barras como Apple Passbook, en F-Droid hay una app excelente llamada Loyalty Card Keychain: https://f-droid.org/en/packages/protect.card_locker/
Puedes ingresar el número manualmente o escanear un código de barras existente, y guardarlo eligiendo entre varios formatos de código de barras, incluido Codabar. Al tocar un elemento en la pantalla principal de la app, muestra el código de barras generado y también sube el brillo de la pantalla.
Quizá porque no hace mucho más que mostrar códigos de barras, la app también abre muy rápido. Solo tiene una función, pero tal vez sea una de mis apps favoritas.
Parece que hoy en día recomiendan usar Catima; la probé un rato y se ve igual de simple y basada en el mismo código.
Es incómodo, pero cumple el objetivo, y su política de privacidad es buena.
No entiendo por qué tiene sentido que los pases deban estar firmados criptográficamente y con una clave conocida por la autoridad certificadora de Apple. Para este tipo de actualizaciones ya existe HTTPS, que está bien soportado.
Puede que Apple quiera que los pases se puedan verificar no solo al momento de actualizarse, sino también offline en el teléfono, pero aun así es raro. Un actor malicioso podría directamente reemplazar el pase en vez de actualizarlo.
No veo en absoluto cómo vincularlo a una cuenta de desarrollador de Apple ayuda con el problema de las actualizaciones.
La única razón que se me ocurre para exigir que sea una entidad aprobada por Apple es la venta de entradas falsas. No hay forma de saber si un archivo pkpass llamado “entrada para concierto X” es real o no, exista o no ese requisito.
Tal vez intenten responder cancelando esa cuenta de desarrollador si reciben denuncias de fraude, pero eso tampoco parece una solución. El costo de una cuenta de desarrollador probablemente sea mucho menor que el dinero que se puede ganar con una estafa antes de ser detectado.
Yo simplemente me envío por correo un PNG del código de barras con el número de mi tarjeta de biblioteca y lo abro en Photos o en la app de Gmail frente al kiosco.
Con la app MakePass hice un pase con mi código de membresía de StarBucks, y cuando me acerco a una tienda a la que voy seguido, el teléfono me sugiere automáticamente el pase de StarBucks en la pantalla bloqueada.
Con las entradas a eventos pasa lo mismo: si defines una ubicación y un rango horario, al llegar al lugar se sugieren automáticamente sin tener que buscar el pase. Cuando termina el evento, deja de sugerirlo y lo mueve a la sección “Expired Passes”, para no desordenar la pantalla principal.
MakePass: https://pvieito.com
Fue una buena entrada de blog, pero termina con “todavía no probé este pase en una biblioteca real”.
Entiendo que es un proyecto hecho por diversión, pero no sé por qué no dedicó los 10 minutos necesarios a probar la solución final antes de compartir el resultado.
Al generar códigos de barras, personalmente prefiero un generador de códigos de barras en PostScript
https://bwipp.terryburton.co.uk/
Resolví este problema tomando una foto de la tarjeta de la biblioteca. Al sacar libros prestados, basta con abrir la foto y mostrarla al escáner
Guardo todas mis identificaciones en el álbum de fotos del teléfono
Si quieres hacer algo parecido en Android, está Catima, disponible en Google Play y F-Droid. Soporta varios tipos de códigos de barras
https://catima.app/
Los escáneres láser baratos de códigos de barras 1D no pueden leer códigos de barras desde una pantalla. Quizá funcionen con eInk
La tarjeta de membresía del supermercado al que voy seguido está basada en código de barras y es incómodo, pero por suerte el lector de códigos de barras emula un teclado, así que simplemente se puede ingresar el código con el teclado
A los escáneres basados en cámara no les importa si la iluminación del código de barras viene de la luz ambiental, de sus propios LED o de una pantalla retroiluminada, pero los sistemas basados en láser dependen del reflejo de su propia luz, así que no funcionan en absoluto con pantallas con retroiluminación activa
Me da curiosidad si funcionarían con e-ink o con pantallas LCD pasivas
Por fuera parece un simple tipo 2D de línea roja, pero parece que el hardware sí importa
Simplemente memoricé el número de cuenta de KCLS. Debería bastar con unos 30 segundos para memorizarlo; dependerá de cada persona, pero creo que este método sería más rápido
Después, en vez de escanear el código de barras, puedes ingresar el número de cuenta, y probablemente tome menos que sacar el teléfono y prepararlo
No sé si SPL funciona de la misma manera. No vivo en Seattle, así que no pude comprobarlo