2 điểm bởi GN⁺ 2023-10-29 | 1 bình luận | Chia sẻ qua WhatsApp
  • Từ mục tiêu giảm bớt thẻ nhựa của Seattle Public Library và King County Library System, tác giả đã phải tự xử lý cấu trúc JSON, hình ảnh và chữ ký của thẻ Wallet trên iPhone
  • Wallet chỉ hỗ trợ QR code, PDF417, Aztec Code và Code 128, nên không thể đưa Codabar của thẻ thư viện vào dưới dạng mã vạch gốc; mã vạch dùng để quét thực tế được lách bằng hình ảnh
  • Thẻ cần được ký bằng khóa được Apple tin cậy, nhưng thay vì trả 99 USD/năm cho tư cách thành viên Apple Developer, tác giả đã tìm chứng chỉ và khóa trong một ứng dụng iOS tạo thẻ có thể hoạt động offline
  • Kết hợp bố cục storeCard, logo theo từng độ phân giải, strip.png Codabar và một mục PKBarcodeFormatCode128 rỗng để vừa hiển thị mã vạch dạng hình ảnh trên màn hình, vừa giữ được hành vi tăng độ sáng màn hình của iOS
  • File .pkpass hoàn chỉnh có thể được tạo bằng cách sinh manifest, ký bằng openssl smime và đóng gói ZIP; dù chưa thử tại thư viện thật, một máy quét riêng đã đọc được mã vạch trên màn hình điện thoại

Mục tiêu: dùng mã vạch thư viện mà không cần thẻ nhựa

  • Seattle Public Library và King County Library System phát hành thẻ nhựa in mã vạch liên kết với tài khoản mượn sách
  • Tính đến năm 2023, phần lớn chức năng của thư viện Seattle là tự phục vụ, và thường có thể nhập thủ công mã vạch thư viện
  • Tuy vậy, việc hiển thị mã vạch ngay lập tức vẫn tiện hơn nhiều, nên mục tiêu là đưa thẻ nhựa hiếm khi dùng vào ứng dụng Wallet trên iPhone thay vì để trong ví

Thẻ Wallet gồm JSON, hình ảnh và chữ ký

  • Ứng dụng Wallet hiện xử lý thẻ thanh toán, giấy tờ tùy thân, khóa, v.v., nhưng khi Passbook ra mắt năm 2012, nó chỉ quản lý “pass”
  • Theo tài liệu về pass của Apple, pass là một file ZIP tự chứa có thể phân phối qua email hoặc web, bên trong gồm JSON và PNG
  • Cấu trúc cốt lõi của file .pkpass khá đơn giản
    • pass.json: mô tả nội dung của pass, không phải hình ảnh
    • manifest.json: danh sách checksum SHA-1 của các file khác
    • signature: chữ ký S/MIME cho nội dung manifest.json
    • Các file hình ảnh được hỗ trợ

Không hỗ trợ Codabar là rào cản đầu tiên

  • Pass Wallet chỉ hỗ trợ bốn loại mã vạch
    • QR code
    • PDF417
    • Aztec Code
    • Code 128
  • Thẻ thư viện dùng định dạng Codabar, vốn được dùng rộng rãi trong các thư viện
  • Có khả năng máy quét mã vạch của thư viện hỗ trợ các mã vạch tuyến tính khác, nhưng Codabar là định dạng duy nhất có thể xem là chắc chắn hoạt động ở mọi chi nhánh
  • Cuối cùng, thay vì dùng tính năng mã vạch gốc của Wallet, cần tạo một hình ảnh Codabar có thể quét được và đưa vào pass

Vấn đề chứng chỉ ký pass

  • Pass Wallet cần chữ ký mật mã và phải được ký bằng khóa được một trong các cơ quan chứng thực của Apple tin cậy
  • Nếu dùng để nhà cung cấp tự động cập nhật pass, chẳng hạn thay đổi cổng lên máy bay hoặc đổi ghế trên vé máy bay, thì yêu cầu ký ở mức nào đó là hợp lý
  • Nếu là nhà phát triển Apple, việc nhận khóa ký pass khá dễ, nhưng tác giả không muốn trả 99 USD/năm chỉ cho việc này

Vì sao các ứng dụng tạo pass sẵn có không phù hợp

  • Đã có nhiều ứng dụng thiết kế pass do những người vốn là Apple Developer tạo ra, nhưng chúng không cung cấp mức kiểm soát cần thiết
  • Kiểm soát kích thước logo

    • Apple cho phép logo ở góc trên bên trái của pass có chiều cao tối đa 50 device-independent pixels
    • Với logo vuông nằm bên phải phần chữ, chiều cao khoảng 40px là phù hợp nhất
    • Nhà phát triển phải cung cấp riêng logo.png, logo@2x.png, logo@3x.png, nhưng các ứng dụng thường chỉ cho chọn một logo duy nhất và không cho kiểm soát tỉ lệ
  • Hành vi độ sáng màn hình

    • Khi mở một pass có mã vạch được hỗ trợ thông thường, màn hình sẽ sáng lên để tăng độ tương phản cho máy quét
    • Để iOS làm màn hình sáng lên mà không hiển thị một mã vạch thực ra không hoạt động, cần chỉnh trực tiếp pass.json
    • Một số ứng dụng có thể tải miễn phí cũng giới hạn số pass có thể lưu vào Wallet và yêu cầu thanh toán

Tìm khóa ký trong ứng dụng tạo pass offline

  • Tải xuống nhiều ứng dụng tạo pass dạng miễn phí ban đầu và kiểm tra xem chúng có tạo được pass khi offline hay không
  • Kết quả thử nghiệm ở chế độ máy bay và tắt Wi-Fi cho thấy ít nhất một ứng dụng có thể tạo pass offline
  • Tác giả không nêu tên cụ thể ứng dụng đó để tránh việc khóa bị thu hồi
  • Quy trình đơn giản hơn dự kiến
    • Vì có thể chạy ứng dụng iOS trên máy Mac Apple silicon, tác giả tải ứng dụng về Mac
    • Tìm file .p12 dễ thấy bên trong gói ứng dụng iOS đã được bọc
    • File PKCS#12 cần mật khẩu nhập, nên chạy strings trên binary chính để tìm chuỗi trông giống mật khẩu
  • Khi kiểm tra thông tin chứng chỉ bằng openssl pkcs12, issuer hiện là Apple Worldwide Developer Relations Certification Authority
  • Cũng cần chuỗi chứng chỉ; chứng chỉ trung gian có thể nằm trong gói ứng dụng và cũng có thể tải xuống từ URL trong trường mở rộng X.509

Cấu hình bố cục pass

  • Kiểu pass phải chọn một trong các loại boarding pass, coupon, event ticket, generic, store card
  • Để đưa vào một hình ảnh ngang lớn, cần bố cục hỗ trợ hình ảnh strip
    • Các ứng viên là coupon, event ticket, store card
    • Chọn store card vì nó giống thẻ thư viện vật lý nhất
  • pass.json chứa các giá trị sau
    • passTypeIdentifier
    • teamIdentifier
    • formatVersion
    • serialNumber
    • organizationName
    • logoText
    • description
    • storeCard
    • backgroundColor
    • foregroundColor
    • sharingProhibited
  • passTypeIdentifierteamIdentifier phải lần lượt khớp với các trường UID, OU trong subject của chứng chỉ Apple
  • Mỗi pass được tạo với cùng passTypeIdentifier cần có serialNumber duy nhất
  • Hình ảnh được chuẩn bị theo từng mục đích sử dụng
    • icon.png là bắt buộc nhưng không hiển thị trên chính pass
    • logo.png là logo hiển thị ở góc trên bên trái
    • Tạo logo.png 40×40, logo@2x.png 80×80, logo@3x.png 120×120
    • Sao chép logo@3x.png thành icon.png
    • Mã vạch đã tạo trước được đưa vào strip.png

Tạo hình ảnh mã vạch Codabar

  • iOS scale và crop strip.png cho vừa khung trên màn hình thiết bị, nên không cần tạo ba phiên bản strip.png theo từng độ phân giải
  • Hai thẻ thư viện dùng A và D làm ký hiệu bắt đầu/kết thúc
  • Cách dễ nhất để xác nhận ký hiệu bắt đầu/kết thúc là dùng máy quét mã vạch; nếu không có, có thể so sánh bằng mắt với bảng mã hóa Codabar trên Wikipedia
  • Không có nhiều trình tạo Codabar dùng ngay trên mạng, nhưng bản thân định dạng này dễ tự triển khai
  • Trong bản nguyên mẫu, tác giả dùng thư viện Barcoders cho Rust để tạo SVG, chỉnh SVG rồi xuất ra PNG
  • Bố cục cuối cùng được thiết kế có xét đến khả năng quét và việc iOS hiển thị thu nhỏ
    • Chiều cao mã vạch được đặt theo đơn vị gấp 2 lần tổng số ký hiệu, bao gồm ký hiệu bắt đầu/kết thúc
    • Ví dụ, số mã vạch 13 chữ số có 15 ký hiệu, nên chiều cao là 30 đơn vị
    • Đặt 15 đơn vị quiet space trước điểm bắt đầu và sau điểm kết thúc
    • Đặt 50 đơn vị padding phía trên và dưới mã vạch
    • Mỗi đơn vị được phóng to thành 8 pixel để iOS luôn thu nhỏ hình ảnh
    • Với ví dụ mã vạch 15 ký hiệu, ảnh cuối cùng cao 1040 pixel và bản thân mã vạch cao 240 pixel
  • Tác giả viết một shell script 69 dòng tạo BMP Codabar theo bố cục trên và dùng sips để chuyển thành PNG
  • Lưu đầu ra của script thành strip.png là đã có hình ảnh mã vạch để đưa vào pass

Hiển thị số mã vạch và cách lách độ sáng màn hình

  • Số mã vạch được hiển thị bên dưới mã vạch bằng secondaryFields
    • key: number
    • label: CARD NUMBER
    • value: số thẻ
  • iOS tăng độ sáng màn hình khi chọn pass có mã vạch để hỗ trợ máy quét
  • Chỉ có Codabar dạng hình ảnh thì iOS không nhận là pass có mã vạch
  • Thêm một mục mã vạch rỗng ở cấp cao nhất của pass.json để lách hành vi này
    • message là chuỗi rỗng
    • formatPKBarcodeFormatCode128
    • messageEncodingiso-8859-1
  • Cách này không hiển thị mã vạch ở cuối pass, nhưng vẫn khiến iPhone tăng độ sáng màn hình như với pass có mã vạch

Ký và đóng gói

  • Sau khi chuẩn bị tất cả file, cần tạo manifest.json
  • manifest.json là một object có tên file làm khóa và checksum SHA-1 làm giá trị
  • Kết hợp sha1sumjq để tạo manifest cho các file PNG và pass.json
  • Việc ký manifest được thực hiện bằng lệnh openssl smime
    • Chứng chỉ signer
    • Private key
    • Chứng chỉ trung gian Apple WWDR
    • Đầu vào manifest.json
    • Đầu ra signature
  • Có thể chỉ định thời điểm ký mong muốn bằng tùy chọn -attime của openssl smime
    • Giá trị tùy chọn là UNIX epoch
    • Ngay cả khi chứng chỉ nhận được từ Apple hoặc tìm được đã hết hạn, vẫn có thể ký theo thời điểm đó
  • Cuối cùng, nén các file sau thành ZIP để tạo .pkpass
    • Các hình ảnh PNG
    • pass.json
    • manifest.json
    • signature

Kết quả thử nghiệm và điểm còn tiếc

  • macOS có công cụ xem trước pass, giúp kiểm tra pass có hợp lệ và nhìn chung hiển thị đúng hay không
  • Nếu không hợp lệ, có thể tìm lỗi trong Console.app
  • Công cụ xem trước không chính xác 100%, nhưng có nút gửi sang iPhone qua iCloud
  • Pass hoàn chỉnh vẫn chưa được thử nghiệm tại thư viện thật
  • Một máy quét mã vạch riêng có thể đọc mã vạch trên màn hình điện thoại giống như đọc thẻ nhựa thật
    • Tuy nhiên, nó đọc tốt khi tăng độ sáng màn hình lên tối đa
    • Mức này sáng hơn độ sáng được tăng nhờ cách lách bằng mã vạch rỗng
  • Việc đặc tả pass hầu như không thay đổi trong 10 năm là điều tốt, nhưng thật đáng tiếc khi cần tư cách thành viên Apple Developer 99 USD/năm chỉ để ký một pass vô hại gồm PNG và JSON
  • Có thể xem một số tính năng pass là cần chữ ký, nhưng với những gì đã làm trong tác vụ này thì lẽ ra không nên cần chữ ký
  • Nếu Apple bổ sung hỗ trợ Codabar cho Wallet, có thể hỗ trợ thẻ thư viện số mà không cần kiểm tra xem toàn bộ máy quét trong hệ thống thư viện có hỗ trợ Code 128 hay không

1 bình luận

 
GN⁺ 2023-10-29
Ý kiến trên Hacker News
  • Lần sau mong là cũng đề cập đến thẻ ORCA. Seattle là một trong những trung tâm công nghệ của Mỹ, vậy mà không chỉ tàu điện/nghệ thống giao thông công cộng nói chung khá tệ, mà về mặt công nghệ còn tụt hậu hơn hầu hết các thành phố lớn tôi từng dùng
    So với bất kỳ thành phố nào ở châu Âu, CDMX hay Denver thì họ đều đi trước Seattle rất xa; cá nhân tôi thấy Denver có lẽ là tốt nhất
    Từng có một trong các cơ quan vận hành ORCA đăng bài blog nói sẽ thêm hỗ trợ NFC cho ứng dụng Android, nhưng bài đó giờ đã biến mất, và vài năm trôi qua vẫn chưa có tính năng đó

    • Vài ngày trước có thông tin rằng Google Wallet sắp hỗ trợ việc này: https://blog.google/products/google-pay/commute-around-the-w...
    • Hệ thống giao thông ở khu vực của chúng tôi gần đây đã thêm thanh toán chạm, nên có thể dùng bất kỳ thẻ tín dụng nào, Apple Pay/Google Pay đều được
      Chỉ cần dùng cùng một thẻ thì việc chuyển tuyến, vé nhiều ngày, v.v. đều được xử lý trơn tru, và hệ thống tự áp mức giá đúng, kiểu như nếu đi quá một số lượt nhất định trong một khoảng thời gian thì sẽ không tính thêm nữa
    • Tôi thắc mắc chi phí duy trì hệ thống thu vé là bao nhiêu, trong khi phần nguồn thu bị mất nếu hầu hết các hệ thống giao thông công cộng chuyển sang đi miễn phí chỉ là một phần mà thôi
    • Liên quan đến chuyện này, chẳng phải có cách sao chép thẻ NFC bằng Android đã root sao?
      Cửa văn phòng tôi mở bằng thẻ NFC nên tôi đã tìm xem có làm được trên iOS không, và theo tôi nhớ thì Apple kiểm soát phần cứng NFC chặt hơn PassKit thông thường, nên ứng dụng thường khó làm được
    • https://info.myorca.com/news/can-i-use-my-phone-to-pay-for-a...
      Họ nói thanh toán chạm sẽ đến với ORCA trong năm 2023, nên nếu điều đó thành sự thật thì giờ chỉ còn khoảng hai tháng nữa
      Google đã đăng rằng Google Wallet sắp hỗ trợ ORCA, nên tôi đang khá lạc quan
  • Nếu bạn dùng Android và không thích việc Google Wallet không cho thêm bất kỳ mã vạch nào như Apple Passbook, trên F-Droid có một ứng dụng rất hay tên là Loyalty Card Keychain: https://f-droid.org/en/packages/protect.card_locker/
    Bạn có thể nhập số trực tiếp hoặc quét mã vạch có sẵn, rồi chọn lưu một trong nhiều định dạng mã vạch, bao gồm cả Codabar. Trên màn hình chính của ứng dụng, chạm vào một mục sẽ hiển thị mã vạch được tạo và tăng độ sáng màn hình
    Có lẽ vì ngoài việc hiển thị mã vạch thì ứng dụng không làm gì nhiều, nên mở cũng rất nhanh. Nó chỉ có một chức năng, nhưng có thể là ứng dụng tôi thích nhất
    Dạo này có vẻ người ta khuyên dùng Catima, tôi dùng thử một lúc thì thấy cũng đơn giản tương tự và có vẻ dựa trên cùng codebase

    • Trên Apple, tôi tạo ‘thẻ’ bằng Pass4Wallet rồi nhập vào Apple Wallet để dùng
      Hơi bất tiện, nhưng đạt mục đích, và chính sách quyền riêng tư cũng tốt
    • Cũng có ứng dụng cho phép đưa mã vạch tùy ý vào Google Wallet: https://play.google.com/store/apps/details?id=color.dev.com....
  • Tôi không hiểu vì sao việc pass phải được ký bằng mật mã học và phải được ký bằng khóa mà tổ chức chứng thực của Apple biết lại hợp lý. Với mục đích cập nhật kiểu này thì đã có HTTPS được hỗ trợ rất tốt rồi
    Có thể Apple muốn pass được xác minh không chỉ lúc cập nhật mà cả khi ngoại tuyến trên điện thoại, nhưng dù vậy vẫn lạ. Kẻ xấu có thể không cập nhật pass mà thay luôn bằng cái khác

    • Chỉ cần pass ban đầu chỉ định khóa công khai được phép cập nhật là đủ
      Tôi hoàn toàn không thấy việc liên kết với tài khoản nhà phát triển Apple giúp gì cho vấn đề cập nhật
      Lý do duy nhất tôi có thể nghĩ ra cho yêu cầu “phải là chủ thể được Apple chấp thuận” là để ngăn bán vé giả. Một tệp pkpass ghi “vé concert X” là thật hay giả thì dù có yêu cầu đó hay không cũng không có cách nào biết được
      Có thể họ muốn xử lý bằng cách hủy tài khoản nhà phát triển đó khi có báo cáo lừa đảo, nhưng điều này cũng không giống một giải pháp. Chi phí tài khoản nhà phát triển rất có thể rẻ hơn nhiều so với số tiền có thể kiếm được từ lừa đảo trước khi bị phát hiện
  • Tôi chỉ gửi email cho chính mình một PNG mã vạch chứa số thẻ thư viện, rồi khi đứng trước kiosk thì mở bằng Photos hoặc ứng dụng Gmail để dùng

    • Pass trong Wallet có thể định nghĩa vị trí và khoảng thời gian mà pass đó sẽ hiện là “có liên quan”
      Tôi đã dùng ứng dụng MakePass để tạo một pass chứa mã thành viên StarBucks, và khi đến gần cửa hàng tôi hay tới, điện thoại tự động gợi ý pass StarBucks trên màn hình khóa
      Vé sự kiện cũng tương tự: nếu chỉ định vị trí và khoảng thời gian, khi đến nơi pass sẽ tự được gợi ý, không cần lục tìm. Khi sự kiện kết thúc, nó không còn được gợi ý nữa và được chuyển sang mục “Expired Passes”, nên không làm rối màn hình chính
      MakePass: https://pvieito.com
    • Khi đọc lướt bài, tôi cứ tìm xem vì sao họ không đơn giản là chụp ảnh
  • Bài blog hay, nhưng kết thúc lại là “tôi vẫn chưa thử pass này ở thư viện thật”
    Tôi hiểu đây là dự án làm cho vui, nhưng không hiểu vì sao trước khi chia sẻ kết quả lại không bỏ ra 10 phút cần thiết để kiểm thử giải pháp cuối cùng

    • Chính đoạn đó làm cả bài bị mất lực. Cảm giác như: “Tôi có một lý thuyết khoa học tuyệt vời về mất oxy khi chạy bộ. Phải thừa nhận là tôi vẫn chưa bắt đầu chạy thử…”
    • Rốt cuộc nó chỉ là một cụm pixel biểu diễn mã vạch sẽ được quét. Miễn là hiển thị đúng thì có gì có thể sai được?
    • Có thể thư viện không mở cửa vào thứ Bảy
  • Khi tạo mã vạch, cá nhân tôi thích trình tạo mã vạch bằng PostScript
    https://bwipp.terryburton.co.uk/

  • Tôi đã giải quyết vấn đề này bằng cách chụp ảnh thẻ thư viện. Khi mượn sách chỉ cần mở ảnh và đưa trước máy quét

    • Tôi đưa ảnh mã vạch vào thông báo dựa trên vị trí, nên khi đến thư viện trong khu là thông báo hiện lên
    • Nếu là mã vạch tĩnh thì chỉ cần có ảnh là đủ. Lợi ích của việc đưa vào Wallet không lớn
      Tôi lưu tất cả giấy tờ tùy thân trong album ảnh trên điện thoại
  • Nếu muốn làm điều tương tự trên Android thì có Catima trên Google Play và F-Droid. Ứng dụng hỗ trợ nhiều loại mã vạch
    https://catima.app/

    • Cũng có thể dùng Google Pay hoặc Samsung Pay
  • Các máy quét mã vạch 1D laser giá rẻ không đọc được mã vạch trên màn hình. eInk thì có thể được
    Thẻ thành viên của siêu thị tôi hay đi dựa trên mã vạch nên khá bất tiện, nhưng may là đầu đọc mã vạch giả lập bàn phím, nên có thể chỉ cần nhập mã bằng bàn phím

    • Theo tôi hiểu, đó là vì các hệ thống cũ không quét bằng cảm biến ảnh số, mà dùng chùm laser quay và một photodiode đơn giản để đọc sự thay đổi độ sáng phản xạ từ các vùng trắng/đen của mã vạch
      Máy quét dựa trên camera không quan tâm ánh sáng chiếu mã vạch là ánh sáng môi trường, LED tích hợp hay màn hình có đèn nền, nhưng hệ thống dựa trên laser phụ thuộc vào phản xạ ánh sáng của chính nó nên hoàn toàn không hoạt động với màn hình có đèn nền chủ động
      Tôi tò mò không biết nó có hoạt động trên e-ink hoặc màn hình LCD thụ động không
    • Tôi đã làm một bộ hiển thị mã vạch giá rẻ bằng Bluetooth MCU và màn hình E-ink, và nó hoạt động tốt với mọi máy quét mã vạch chỉ hỗ trợ 1D
    • Mỗi lần thấy máy quét cầm tay của Costco đọc mã vạch hội viên được in rất hẹp và nhỏ trên thẻ hội viên nhanh và dễ đến thế, tôi lại thấy ấn tượng
      Nhìn bề ngoài có vẻ như loại 2D với một vạch đỏ đơn giản, nhưng đúng là phần cứng quan trọng
  • Tôi chỉ học thuộc số tài khoản KCLS. Chắc chỉ mất khoảng 30 giây để nhớ, tùy người, nhưng tôi nghĩ cách này sẽ nhanh hơn
    Sau đó chỉ cần nhập số tài khoản thay vì quét mã vạch, có khả năng còn nhanh hơn thời gian lấy điện thoại ra chuẩn bị
    Không biết SPL có giống vậy không. Tôi không sống ở Seattle nên không kiểm tra được

    • Nếu không muốn học thuộc, có thể lưu nó trong trình quản lý mật khẩu tùy thích