2 pontos por GN⁺ 2023-10-29 | 1 comentários | Compartilhar no WhatsApp
  • 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, um strip.png com Codabar e uma entrada vazia PKBarcodeFormatCode128, 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 .pkpass final pôde ser criado com geração de manifest, assinatura via openssl smime e 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 é simples
    • pass.json: descreve o conteúdo do pass que não é imagem
    • manifest.json: lista de checksums SHA-1 dos outros arquivos
    • signature: assinatura S/MIME sobre o conteúdo de manifest.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.png e logo@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 .p12 chamativo
    • Como arquivos PKCS#12 exigem senha de importação, foi executado strings no 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, generic e store card
  • Para inserir uma imagem horizontal grande, era preciso um layout com suporte a imagem strip
    • As opções eram coupon, event ticket e store card
    • Foi escolhido store card por ser o formato mais parecido com um cartão físico de biblioteca
  • O pass.json continha os seguintes valores
    • passTypeIdentifier
    • teamIdentifier
    • formatVersion
    • serialNumber
    • organizationName
    • logoText
    • description
    • storeCard
    • backgroundColor
    • foregroundColor
    • sharingProhibited
  • passTypeIdentifier e teamIdentifier precisam corresponder, respectivamente, aos campos UID e OU do subject do certificado Apple
  • Cada pass criado com o mesmo passTypeIdentifier precisa ter um serialNumber único
  • As imagens foram preparadas conforme a finalidade
    • icon.png é obrigatório, mas não aparece no próprio pass
    • logo.png é o logo exibido no canto superior esquerdo
    • Foram gerados logo.png em 40×40, logo@2x.png em 80×80 e logo@3x.png em 120×120
    • logo@3x.png foi copiado como icon.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.png para caber na caixa da tela do dispositivo, não foi necessário criar três versões por resolução de strip.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 barras
    • key: number
    • label: CARD NUMBER
    • value: 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.json
    • message era uma string vazia
    • format era PKBarcodeFormatCode128
    • messageEncoding era iso-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 sha1sum e jq para gerar o manifest dos arquivos PNG e do pass.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 -attime do openssl smime permite 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.json
    • manifest.json
    • signature

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

 
GN⁺ 2023-10-29
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

    • Foi publicado há alguns dias que o Google Wallet deve oferecer suporte a isso em breve: https://blog.google/products/google-pay/commute-around-the-w...
    • O sistema de transporte da nossa região adicionou recentemente pagamento por aproximação, permitindo usar qualquer cartão de crédito, Apple Pay ou Google Pay
      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
    • Fico curioso para saber quanto custa manter um sistema de cobrança de tarifas, em comparação com a parcela de receita que a maioria dos sistemas de transporte público perderia ao mudar para tarifa zero
    • Relacionado a isso: não haveria uma forma de fazer clonagem de cartão NFC com um Android com root?
      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
    • https://info.myorca.com/news/can-i-use-my-phone-to-pay-for-a...
      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

    • Na Apple, eu crio um “cartão” com o Pass4Wallet e depois importo para o Apple Wallet
      É meio inconveniente, mas cumpre o objetivo, e a política de privacidade é boa
    • Também existe um app que permite colocar qualquer código de barras que você quiser no Google Wallet: https://play.google.com/store/apps/details?id=color.dev.com....
  • 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

    • Bastaria fazer com que o passe inicial especificasse a chave pública autorizada a permitir atualizações
      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

    • Passes do Wallet podem definir o local e intervalo de tempo em que aquele passe aparece como “relevante”
      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
    • Enquanto passava os olhos pelo texto, fiquei procurando por que a pessoa simplesmente não tirou uma foto
  • 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

    • Essa parte tirou a força do texto inteiro. Ficou parecendo: “Tenho uma excelente teoria científica sobre perda de oxigênio durante a corrida. Admito que ainda não comecei a correr, mas…”
    • No fim das contas, é só um conjunto de pixels representando um código de barras que será escaneado. Se ele for exibido corretamente, o que poderia dar errado?
    • Talvez a biblioteca não abra aos sábados
  • 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

    • Coloquei a imagem do código de barras em um lembrete baseado em localização, então, quando vou à biblioteca do bairro, ela aparece como notificação
    • Se for um código de barras estático, só a foto já basta. A vantagem de colocá-lo na Wallet não é tão grande
      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/

    • Também dá para usar Google Pay ou Samsung Pay
  • 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

    • Pelo que entendo, isso acontece porque sistemas antigos não escaneiam com um sensor de imagem digital; eles leem as variações de brilho refletidas pelas áreas brancas/pretas do código de barras usando um feixe de laser giratório e um fotodiodo simples
      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
    • Fiz um exibidor de código de barras barato com um MCU Bluetooth e um display E-ink, e ele funciona bem em todos os scanners de código de barras dedicados a 1D
    • Toda vez fico impressionado com a rapidez e a facilidade com que os scanners portáteis da Costco leem o código de barras de membro impresso de forma bem estreita e pequena no cartão
      À 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

    • Se não quiser memorizar, basta salvá-lo no gerenciador de senhas de sua preferência