- Com o objetivo da Seattle Public Library e do King County Library System de reduzir cartões plásticos, foi preciso lidar diretamente com a estrutura de JSON, imagens e assinatura dos passes do Wallet no iPhone
- O Wallet só oferece suporte a QR code, PDF417, Aztec Code e Code 128, então não dava para usar o Codabar do cartão da biblioteca como código de barras nativo; o código real para leitura foi contornado com uma imagem
- O pass precisava ser assinado com uma chave confiável pela Apple, mas em vez de pagar a assinatura anual de US$ 99 do Apple Developer, foram encontrados e usados certificado e chave em um app de passes para iOS que permitia geração offline
- Combinando o layout
storeCard, logos por resolução, umstrip.pngcom Codabar e uma entrada vaziaPKBarcodeFormatCode128, foi possível mostrar o código de barras em imagem na tela e ainda manter o comportamento do iOS de aumentar o brilho da tela - O
.pkpassfinal pôde ser criado com geração de manifest, assinatura viaopenssl smimee empacotamento ZIP; ainda não foi testado em uma biblioteca real, mas um scanner separado conseguiu ler o código de barras na tela do celular
Objetivo: usar o código de barras da biblioteca sem cartão plástico
- A Seattle Public Library e o King County Library System emitem cartões plásticos com código de barras vinculados à conta de empréstimo
- Em 2023, a maior parte das funções da biblioteca de Seattle era de autoatendimento, e normalmente é possível digitar manualmente o código de barras da biblioteca
- Ainda assim, mostrar o código de barras na hora é muito mais prático, então o objetivo era colocá-lo no app Wallet do iPhone em vez de carregar na carteira um cartão plástico que quase nunca é usado
Os passes do Wallet são compostos por JSON, imagens e assinatura
- Hoje o app Wallet lida com cartões de pagamento, identidades, chaves e mais, mas quando o Passbook foi lançado em 2012 ele gerenciava apenas “passes”
- Segundo a documentação de passes da Apple, um pass é um arquivo ZIP autocontido que pode ser distribuído por e-mail ou web, contendo JSON e PNGs
- A estrutura principal de um arquivo
.pkpassé simplespass.json: descreve o conteúdo do pass que não é imagemmanifest.json: lista de checksums SHA-1 dos outros arquivossignature: assinatura S/MIME sobre o conteúdo demanifest.json- arquivos de imagem compatíveis
A falta de suporte a Codabar foi a primeira barreira
- Os passes do Wallet oferecem suporte a apenas quatro tipos de código de barras
- QR code
- PDF417
- Aztec Code
- Code 128
- O cartão da biblioteca usava o formato Codabar, amplamente adotado por bibliotecas
- É possível que os leitores de código de barras da biblioteca aceitem outros formatos lineares, mas o único formato que certamente funcionaria em todas as unidades era o Codabar
- No fim, em vez de usar o recurso nativo de código de barras do Wallet, foi preciso gerar uma imagem de Codabar legível e colocá-la dentro do pass
O problema do certificado de assinatura do pass
- Passes do Wallet exigem assinatura criptográfica e precisam ser assinados com uma chave confiável por uma das autoridades certificadoras da Apple
- Para usos como atualização automática de portão ou assento em cartões de embarque, essa assinatura faz certo sentido
- Quem já é desenvolvedor Apple consegue obter uma chave de assinatura de pass com relativa facilidade, mas não havia intenção de pagar US$ 99 por ano só para isso
Por que os apps existentes de criação de passes não serviram
- Já existiam vários apps de design de passes feitos por pessoas que já eram desenvolvedoras Apple, mas eles não ofereciam o nível de controle necessário
-
Controle do tamanho do logo
- A Apple permite no máximo 50 pixels independentes de dispositivo para o logo no canto superior esquerdo do pass
- O logo quadrado ao lado do texto ficava ideal com cerca de 40 px de altura
- O desenvolvedor precisa fornecer
logo.png,logo@2x.pngelogo@3x.png, mas os apps normalmente só permitem escolher um único logo, sem controle de escala
-
Comportamento de brilho da tela
- Quando se abre um pass com um código de barras compatível, a tela aumenta o brilho para melhorar o contraste para scanners
- Para fazer o iOS aumentar o brilho sem exibir um código de barras que de fato não funcionaria, era preciso mexer diretamente no
pass.json - Até apps gratuitos para download às vezes limitavam a quantidade de passes que podiam ser salvos no Wallet e exigiam pagamento
Encontrando a chave de assinatura em um app de geração offline
- Foram baixados vários apps gratuitos de criação de passes para verificar se conseguiam gerar passes também offline
- Nos testes em modo avião com o Wi‑Fi desativado, pelo menos um app conseguiu criar o pass offline
- O nome específico desse app não é revelado para evitar que a chave seja revogada
- O processo acabou sendo mais simples do que o esperado
- Como apps de iOS podem ser executados em Macs com Apple silicon, o app foi baixado no Mac
- Dentro do bundle encapsulado do app de iOS, foi encontrado um arquivo
.p12chamativo - Como arquivos PKCS#12 exigem senha de importação, foi executado
stringsno binário principal para encontrar uma string que parecia ser a senha
- Ao verificar as informações do certificado com
openssl pkcs12, o emissor aparecia como Apple Worldwide Developer Relations Certification Authority - A cadeia de certificados também era necessária; o certificado intermediário poderia estar no bundle do app e também podia ser baixado pela URL presente nos campos de extensão X.509
Montando o layout do pass
- Os estilos de pass exigem escolher entre
boarding pass,coupon,event ticket,genericestore card - Para inserir uma imagem horizontal grande, era preciso um layout com suporte a imagem
strip- As opções eram
coupon,event ticketestore card - Foi escolhido store card por ser o formato mais parecido com um cartão físico de biblioteca
- As opções eram
- O
pass.jsoncontinha os seguintes valorespassTypeIdentifierteamIdentifierformatVersionserialNumberorganizationNamelogoTextdescriptionstoreCardbackgroundColorforegroundColorsharingProhibited
passTypeIdentifiereteamIdentifierprecisam corresponder, respectivamente, aos camposUIDeOUdo subject do certificado Apple- Cada pass criado com o mesmo
passTypeIdentifierprecisa ter umserialNumberúnico - As imagens foram preparadas conforme a finalidade
icon.pngé obrigatório, mas não aparece no próprio passlogo.pngé o logo exibido no canto superior esquerdo- Foram gerados
logo.pngem 40×40,logo@2x.pngem 80×80 elogo@3x.pngem 120×120 logo@3x.pngfoi copiado comoicon.png- O código de barras gerado previamente foi colocado em
strip.png
Criando a imagem do código de barras Codabar
- Como o iOS redimensiona e recorta
strip.pngpara caber na caixa da tela do dispositivo, não foi necessário criar três versões por resolução destrip.png - Os dois cartões de biblioteca usavam A e D como símbolos de início e fim
- Esses símbolos de início e fim são mais fáceis de confirmar com um scanner de código de barras, mas também podem ser verificados visualmente com a tabela de codificação Codabar da Wikipedia
- Não há muitos geradores de Codabar prontos para uso online, mas o formato em si é fácil de implementar manualmente
- No protótipo, a biblioteca Barcoders para Rust foi usada para gerar um SVG, que depois foi ajustado e exportado para PNG
- O layout final foi definido levando em conta a legibilidade no scanner e a forma como o iOS reduz a imagem na exibição
- A altura do código de barras foi definida em unidades equivalentes ao dobro da quantidade total de símbolos, incluindo os símbolos inicial e final
- Por exemplo, um número de código de barras com 13 dígitos tem 15 símbolos, então a altura foi definida em 30 unidades
- Foi deixado um quiet space de 15 unidades antes do início e depois do fim
- Foram adicionados 50 unidades de padding acima e abaixo do código de barras
- Cada unidade foi ampliada para 8 pixels para que o iOS sempre reduzisse a imagem ao exibi-la
- No exemplo de um código com 15 símbolos, a imagem final tinha 1040 pixels de altura e o código em si tinha 240 pixels
- Foi escrito um script shell de 69 linhas para criar um BMP Codabar com esse layout e convertê-lo em PNG com
sips - Salvando a saída do script como
strip.png, o código de barras em imagem ficava pronto para entrar no pass
Exibição do número do cartão e contorno do brilho da tela
- O número do código de barras era exibido em
secondaryFields, abaixo do código de barraskey:numberlabel:CARD NUMBERvalue: número do cartão
- Quando o iOS seleciona um pass com código de barras, ele aumenta o brilho da tela para ajudar o scanner
- Só o Codabar em imagem não faz o iOS entender que há um código de barras
- Para contornar isso, foi definida uma entrada de código de barras vazia no nível superior de
pass.jsonmessageera uma string vaziaformateraPKBarcodeFormatCode128messageEncodingeraiso-8859-1
- Dessa forma, o pass não mostrava um código de barras na parte inferior, mas o iPhone ainda aumentava o brilho como se houvesse um código de barras compatível
Assinatura e empacotamento
- Depois de preparar todos os arquivos, era preciso gerar
manifest.json manifest.jsoné um objeto cujas chaves são nomes de arquivo e os valores são checksums SHA-1- Foi usada uma combinação de
sha1sumejqpara gerar o manifest dos arquivos PNG e dopass.json - A assinatura do manifest foi feita com o comando
openssl smime- certificado do signer
- chave privada
- certificado intermediário Apple WWDR
- entrada
manifest.json - saída
signature
- A opção
-attimedoopenssl smimepermite definir o horário desejado da assinatura- O valor da opção é um UNIX epoch
- Mesmo que o certificado obtido da Apple ou encontrado já tenha expirado, ainda é possível assinar usando aquele instante no tempo
- Por fim, os arquivos a seguir foram empacotados em ZIP para formar o
.pkpass- imagens PNG
pass.jsonmanifest.jsonsignature
Resultado dos testes e pontos negativos
- O macOS tem uma ferramenta de pré-visualização de passes que permite verificar se o pass é válido e se, em geral, aparece corretamente
- Se ele for inválido, é possível encontrar erros no
Console.app - A ferramenta de pré-visualização não é 100% precisa, mas inclui um botão para enviar o pass ao iPhone via iCloud
- O pass concluído ainda não foi testado em uma biblioteca real
- Um scanner de código de barras separado conseguiu ler na tela do celular o código de barras da mesma forma que lia o cartão plástico real
- Mas a leitura funcionava bem quando o brilho da tela estava no máximo
- Isso era mais claro do que o brilho ativado pelo contorno com código de barras vazio
- É positivo que a especificação do pass quase não tenha mudado em 10 anos, mas é frustrante precisar de uma assinatura Apple Developer de US$ 99 por ano para assinar um pass inofensivo feito de PNG e JSON
- Algumas funções de passes realmente podem justificar a exigência de assinatura, mas o trabalho feito aqui não deveria precisar dela
- Se a Apple adicionasse suporte a Codabar ao Wallet, seria possível oferecer cartões digitais de biblioteca sem ter de auditar se todos os scanners do sistema de bibliotecas aceitam Code 128
1 comentários
Comentários do Hacker News
Na próxima, seria legal se também abordassem o cartão ORCA. Seattle é um dos centros tecnológicos dos EUA, mas o metrô/transporte público em si não só é ruim, como também fica atrás, tecnologicamente, de quase todos os sistemas de transporte público das grandes cidades que já usei
Comparado com qualquer cidade da Europa, CDMX ou Denver, todos estão muito à frente de Seattle; pessoalmente, acho que Denver foi o melhor
Uma das entidades que operam o ORCA já publicou um post no blog dizendo que adicionaria suporte a NFC no app Android, mas esse post sumiu e, anos depois, o recurso ainda não existe
Se você usar sempre o mesmo cartão, ele lida de forma fluida com integrações, passes de vários dias e afins, e aplica automaticamente a tarifa correta, por exemplo deixando de cobrar depois de um certo número de viagens dentro de um período específico
A porta do meu escritório abre com cartão NFC, então pesquisei se isso era possível no iOS e, pelo que lembro, a Apple controla o hardware NFC de forma mais rígida do que o PassKit comum, então seria difícil para apps comuns
Eles disseram que o pagamento por aproximação chegaria ao ORCA ainda em 2023, então restam mais ou menos dois meses para isso se tornar verdade
Como o Google publicou que o ORCA será compatível em breve com o Google Wallet, estou otimista
Para usuários de Android que não gostam do fato de o Google Wallet não permitir adicionar qualquer código de barras como o Apple Passbook, há um excelente app no F-Droid chamado Loyalty Card Keychain: https://f-droid.org/en/packages/protect.card_locker/
Você pode digitar os números manualmente ou escanear um código de barras existente, escolher entre vários formatos de código de barras, incluindo Codabar, e salvar. Ao tocar em um item na tela principal do app, ele mostra o código de barras gerado e também aumenta o brilho da tela
Talvez por não fazer muita coisa além de exibir códigos de barras, o app abre muito rápido. Ele só tem uma função, mas talvez seja um dos meus apps favoritos
Hoje em dia, parece que recomendam usar o Catima; usei por pouco tempo e ele parece igualmente simples e baseado na mesma base de código
É meio inconveniente, mas cumpre o objetivo, e a política de privacidade é boa
Não entendo por que faz sentido exigir que passes sejam assinados criptograficamente e que sejam assinados com uma chave conhecida pela autoridade certificadora da Apple. Para esse tipo de atualização, já existe o HTTPS, que é bem suportado
Talvez a Apple queira que os passes sejam verificáveis não só no momento da atualização, mas também offline no celular, mas ainda assim é estranho. Um agente malicioso poderia simplesmente substituir o passe inteiro em vez de atualizá-lo
Não vejo como vinculá-lo a uma conta de desenvolvedor da Apple ajuda em nada no problema das atualizações
O único motivo que consigo imaginar para exigir que seja uma entidade aprovada pela Apple é a venda de ingressos falsos. Não há como saber se um arquivo pkpass chamado “ingresso do show X” é real ou não, com ou sem esse requisito
Talvez seja uma tentativa de reagir a denúncias de fraude cancelando a conta de desenvolvedor em questão, mas isso também não parece uma solução. O custo de uma conta de desenvolvedor provavelmente é muito menor do que o dinheiro que se pode ganhar com golpes antes de ser pego
Eu simplesmente mando para mim mesmo por e-mail um PNG do código de barras com o número do cartão da biblioteca e, na frente do quiosque, abro no app Photos ou no Gmail
Criei com o app MakePass um passe contendo meu código de membro do StarBucks, e quando chego perto de uma loja que frequento, o celular sugere automaticamente o passe do StarBucks na tela bloqueada
Com ingressos de eventos é a mesma coisa: se você define local e intervalo de tempo, quando chega ao local o passe é sugerido automaticamente, sem precisar ficar procurando por ele. Depois que o evento termina, ele deixa de ser sugerido e é movido para a seção “Expired Passes”, sem poluir a tela principal
MakePass: https://pvieito.com
Foi um bom post de blog, mas termina com “ainda não testei este passe em uma biblioteca de verdade”
Entendo que tenha sido um projeto por diversão, mas não entendo por que não gastar os 10 minutos necessários para testar a solução final antes de compartilhar o resultado
Ao criar códigos de barras, pessoalmente prefiro um gerador de códigos de barras em PostScript
https://bwipp.terryburton.co.uk/
Resolvi esse problema tirando uma foto do cartão da biblioteca. Na hora de pegar livros emprestados, basta abrir a foto e aproximá-la do scanner
Guardo todos os meus documentos de identidade no álbum de fotos do celular
Se quiser fazer algo parecido no Android, existe o Catima, disponível no Google Play e no F-Droid. Ele suporta vários tipos de códigos de barras
https://catima.app/
Scanners de código de barras 1D a laser baratos não conseguem ler códigos de barras na tela. Talvez funcionem em eInk
O cartão de membro do supermercado que frequento é baseado em código de barras, o que é inconveniente, mas felizmente o leitor de código de barras emula um teclado, então dá para simplesmente digitar o código pelo teclado
Scanners baseados em câmera não se importam se a iluminação do código de barras vem da luz ambiente, de LEDs próprios ou de uma tela retroiluminada, mas sistemas baseados em laser dependem do reflexo da própria luz, então não funcionam de jeito nenhum em telas com retroiluminação ativa
Fico curioso para saber se funcionariam em e-ink ou em displays LCD passivos
À primeira vista, parece um tipo 2D simples de linha vermelha, mas aparentemente o hardware faz diferença mesmo
Simplesmente memorizei o número da conta KCLS. Deve levar uns 30 segundos para decorar; varia de pessoa para pessoa, mas acho que esse método é mais rápido
Depois disso, em vez de escanear o código de barras, basta digitar o número da conta, e provavelmente leva menos tempo do que pegar o celular e deixá-lo pronto
Não sei se a SPL funciona do mesmo jeito. Como não moro em Seattle, não consegui verificar