Skip to content

Instantly share code, notes, and snippets.

@safiire
Created September 10, 2021 04:37
Show Gist options
  • Save safiire/46d4f76b2718102f3bf6bd386acbbd53 to your computer and use it in GitHub Desktop.
Save safiire/46d4f76b2718102f3bf6bd386acbbd53 to your computer and use it in GitHub Desktop.
Decode a Vaccine Passport QR code and Display Contents
#!/usr/bin/env ruby
require 'base64'
require 'json'
require 'zlib'
# Requirements:
# Debian: apt-get install zbar-tools
# OSX: brew install zbar
class DecodePassport
Result = Struct.new(:header, :passport, :signature)
def self.call(filename)
new.call(filename)
end
def call(filename)
header, payload, signature = decode_jws(decode_shc(decode_qr(filename)))
Result.new(
JSON.parse(header),
JSON.parse(deflate_payload(payload)),
signature.bytes.map { |b| '%02x' % b }.join
)
end
private
def decode_qr(filename)
`zbarimg --raw "#{filename}" 2>/dev/null`.tap do
fail('Failed to decode QR') unless $?.success?
end
end
def decode_shc(shc)
shc.scan(/\d{2}/).map { |digit| (digit.to_i + 45).chr }.join
end
def decode_jws(jws)
jws.split('.').map { |part| Base64.urlsafe_decode64(part) }
end
def deflate_payload(payload)
Zlib::Inflate.new(-15).inflate(payload)
end
end
if ARGV.size.zero?
STDERR.puts("Usage: #{$0} <imagefile>")
exit(1)
end
result = DecodePassport.call(ARGV.first)
puts 'Header:'
puts JSON.pretty_generate(result.header)
puts 'Signature:'
puts result.signature
puts 'Passport:'
puts JSON.pretty_generate(result.passport)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment