B4A Library CovidPass - the reader

see bottom of this post for "important" update.
LOOK FURTHER DOWN FOR UPDATED VERSION. MAYBE POST #12
LOOK EVEN FURTHER DOWN FOR A LITTLE JSON PARSING ROUTINE AND EXPLANATION
LOOK EVEN FURTHER FURTHER FOR A PARSING CLASS. IN ADDITION TO THE EU GREEN PASS, I'VE ADDED THE FRENCH SANIPASS)
here's a fun project that cost me several nights' sleep. it reads what i hear is called a covid pass.

my government and many of my fellow citizens don't believe covid is real, so we're not allowed to have any covid-themed artifacts, such as proof one has been vaccinated (against what, right?). but they say other countries have such proofs.

i've tested it with some known, valid covid passes. there are none here, so i'm limited. try it and report back if you like.

a couple notes:
the library merely decodes the pass. it doesn't verify its validity. you need the public keys for that. and it doesn't generate a fake pass, since you'll only blame me if you're caught counterfeiting them.

i didn't attach a qr code scanner. we have a ton thanks to member schoeman's tireless work. once you have read the qr code, you feed the decoded string to my reader. unzip the attached to your additional libraries folder and add the library to your project. initialize it and call the decodePass() method. it should fit in with any scanner app you've written. just add the decodePass() with what the qr code says you scanned. display the result in a label or - why not - a webview!:)

images of covid qr codes that i found online were mostly bogus. the current version of the decoded string must begin with "HC1:". if you don't see that when you scan the qr code, don't bother with anything else. and even if "HC1:" is there, the code has to have been decoded correctly. i've tried with both google's version of zxing and zxing's own qr code reader. google always came back with something. zxing had some trouble occasionally, but i was scanning images from a browser. so, to be clear, if the decoded qr code returns a string beginning with "HC1:", then you call the decoder, passing the string. that "header" may change or other headers may exists for different types of covid-related digital forms. who knows what condition we'll all be in by that time.

the resultant string comes back to you as a json string (or json-like). i didn't feed it to a json parser, but it certainly looked like json.
see further down regarding json. it's dealt with in version 0.02, found below. the eu uses json, but the decoder returns the result in a slightly different format.

my interest at this point is hearing if valid passes are successfully decoded. as i said, i have virtually no access to such documents. validation seems pointless unless you're a volunteer covid policeman looking for fake passes. basically, if i see some kind of scannable code, i like to scan it. it's annoying when i can't decode it, like they're trying to keep something from me. if you find a covid pass on the ground, you can decode it with your app and claim your reward. maybe you should wear gloves before picking it up.

the library is govidpass - with a "g". i don't want to be sued by somebody who has copyrighted the word "covid"

update: the library itself is quite small. unfortunately the required dependencies put me over the limit for posting here. if you want a functioning library, download from here: ""

(i had to put the url in quotes because something very strange happened, and i couldn't make it go away)


there is a readme which basically instructs you to copy the jars to your additional libraries folder and to add some compiler options to your app. (thanks to yiankos1 for the heads up. and apologies for the inconvenience)
 
Last edited:

drgottjr

Expert
Licensed User
Longtime User
they're supposed to be in there. are you saying there was a problem?
 

yiankos1

Well-Known Member
Licensed User
Longtime User
they're supposed to be in there. are you saying there was a problem?
It's asking jars when you try to compile. It's asking all the jars that exists in depends on xml file
 

drgottjr

Expert
Licensed User
Longtime User
i'll take a look. back later.
 

drgottjr

Expert
Licensed User
Longtime User
minor disaster. i misread the size of the .zip and thought the dependencies were there. i've corrected my first post with new instructions. you'll need to
go to google drive to get the correct .zip it's here
i rebuilt my test to make sure. seems ok. sorry for the hassle.
 

yiankos1

Well-Known Member
Licensed User
Longtime User
minor disaster. i misread the size of the .zip and thought the dependencies were there. i've corrected my first post with new instructions. you'll need to
go to google drive to get the correct .zip it's here
i rebuilt my test to make sure. seems ok. sorry for the hassle.
Just tried it! It is working fine and reads all fields from green pass. Do you think i can handle the result like json?
 

drgottjr

Expert
Licensed User
Longtime User
do you use the JSON object (parser and generator)? the result looks like json. it's definitely a type of map (the final stage of decoding), so json would fit. i'll see if the result i have parses correctly.
 

drgottjr

Expert
Licensed User
Longtime User
it is json. but you need to know the format. if you just display the result on the device, it's pretty clear what the fields mean. but some of the keys are pretty obscure. eg key "1". what's that? (it so happens it's the country of origin, but some of the other keys are not evident) you'd need the specification. i'm guessing it's available...

in any case, it does parse, and you could use the JSON library. i'll see if i can find the spec. it might be a useful addition to the library.
 

Xfood

Expert
Licensed User
hello @drgottjr , in europe the grennpass specification and on this link, together with a source in kotlin, it would be nice to bring everything to b4x.

 

drgottjr

Expert
Licensed User
Longtime User
thanks for the link. i could be mistaken, but i don't think b4x works the way you think it does in this case.
 

drgottjr

Expert
Licensed User
Longtime User
ok, it turns out that the decoded certificate is not a json string. it's cbor (please!). it's like a json string without json rules. which means, it will break your json parser. the cbor string (if it's not already a valid json string) has to be converted to json. a test for valid jsonness and eventual converson has been added to version 0.02 of the govidpass library. herewith attached. you'll still have to get everything else from the download url given at top. after you've copied the contents to your additional
libraries folder, you can download version 0.02 and just overwrite version 0.01.

again, my testing laboratory is basically empty since i have few sample passes to work with. luckily, one of the samples was "pure" cbor. it failed json parsing, but once it was converted from cbor, all was good.
 

Attachments

  • govidpass.zip
    2.8 KB · Views: 227

ismamokan

Member
This qrcode vax certificate to decode for testing purposes only.
 

Attachments

  • myvaxcertificate.png
    myvaxcertificate.png
    320.7 KB · Views: 203
Last edited:

drgottjr

Expert
Licensed User
Longtime User
i've managed to find a few more green pass images online. samples from different countries. the certificate can cover vaccination, testing or so-called recovery. you don't know which one you're getting until you decode it, convert to json (if necessary) and then parse it. which means, you would normally throw exceptions by trying to parse an element that isn't in the certificate. so i'm attaching a parsing routine which you can add to your project after getting the decoded result back. just call:
B4X:
parse( resulting_json_string)

here's the routine:
B4X:
Sub parse( s As String )

    Dim partialparse As StringBuilder
    partialparse.Initialize

    Dim json As JSONParser
    json.Initialize( s )
    Dim walker As Map = json.NextObject
    Dim country As String = walker.Get("1")
    Dim rec260 As Map = walker.Get("-260")
    Dim rec1 As Map = rec260.Get("1")

    Dim name As Map = rec1.Get("nam")
    partialparse.Append( "certificate issued in: " & country & "  for: " & name.Get("fn") & ", " & name.Get("gn") & " born: " & rec1.Get("dob")).Append(CRLF)


    Try
        Dim vacclist As List = rec1.Get("v")  ' vaccination
        For Each rec As Map In vacclist
            Dim a As Int = rec.Get("tg")
            Dim against As String
            If a = 840539006 Then
                against = "Covid"
            Else
                against = "unknown target"
            End If
           
            Dim d As Int = rec.Get("dn")
           
            partialparse.Append( "date of vaccination: " & rec.Get("dt") & " where: " & rec.Get("co") & " dose #" & d & " against: " & against).Append(CRLF)
        Next
    Catch
'    Log("no vaccination or vaccination parsing error: " & LastException)
    End Try

    Try
        Dim testlist As List = rec1.Get("t")
       
        For Each rec As Map In testlist
            Dim t As Int = rec.Get("tg")
            Dim against As String
            If t = 840539006 Then
                against = "Covid"
            Else
                against = "unknown target"
            End If
           
            Dim r As Int = rec.Get("tr")
            Dim testresult As String
            If r = 260415000 Then
                testresult = "unrecorded"
            Else
                testresult = "unknown"
            End If
            partialparse.Append( "date of test: " & rec.Get("sc") & " where: " & rec.Get("co") & " against: " & against & " result: " & testresult).Append(CRLF)
        Next
    Catch
'        Log("no test or test parsing error: " & LastException)
    End Try

    Try
        Dim recoverylist As List = rec1.Get("r")
       
        For Each rec As Map In recoverylist
            Dim c As Int = rec.Get("tg")
            Dim rfrom As String
            If c = 840539006 Then
                rfrom = "Covid"
            Else
                rfrom = "unknown target"
            End If
           
            partialparse.Append( "recovery from: " & rfrom & " valid until: " & rec.Get("du")).Append(CRLF)
        Next
    Catch
'        Log("no recovery or recovery parsing error: " & LastException)
    End Try

    Log( partialparse.ToString )   ' or do what you want with it
   
End Sub

it's deliberately repetitive and simplistic. it's an example, not exemplary. for those who have trouble with json, it might help in dealing with missing elements.

my understanding is that the validation process requires "public" keys which may not be available to the public... the certificate number which needs to be validated is part of the decoded json string. i didn't include it in the parsing example, but you can see it in the json string. if i can obtain the keys, i'll add a verification method to the library.

by the way, documentation about the certificates is online HERE. don't be fooled by constant references to json. what you end up with is not json. the eu spec uses json, but part of the decoding process uses something different. that something (cbor) is the result of the decoding. sometimes it can parse like json, but what it is often breaks a fundamental rule of json: names must be strings. it has to be converted (back) to json or it will break a json parser. otherwise, it's certainly readable as a json-like string.
 
Last edited:

ismamokan

Member
i've managed to find a few more green pass images online. samples from different countries. the certificate can cover vaccination, testing or so-called recovery. you don't know which one you're getting until you decode it, convert to json (if necessary) and then parse it. which means, you would normally throw exceptions by trying to parse an element that isn't in the certificate. so i'm attaching a parsing routine which you can add to your project after getting the decoded result back. just call:
B4X:
parse( resulting_json_string)

here's the routine:
B4X:
Sub parse( s As String )

    Dim partialparse As StringBuilder
    partialparse.Initialize

    Dim json As JSONParser
    json.Initialize( s )
    Dim walker As Map = json.NextObject
    Dim country As String = walker.Get("1")
    Dim rec260 As Map = walker.Get("-260")
    Dim rec1 As Map = rec260.Get("1")

    Dim name As Map = rec1.Get("nam")
    partialparse.Append( "certificate issued in: " & country & "  for: " & name.Get("fn") & ", " & name.Get("gn") & " born: " & rec1.Get("dob")).Append(CRLF)


    Try
        Dim vacclist As List = rec1.Get("v")  ' vaccination
        For Each rec As Map In vacclist
            Dim a As Int = rec.Get("tg")
            Dim against As String
            If a = 840539006 Then
                against = "Covid"
            Else
                against = "unknown target"
            End If
           
            Dim d As Int = rec.Get("dn")
           
            partialparse.Append( "date of vaccination: " & rec.Get("dt") & " where: " & rec.Get("co") & " dose #" & d & " against: " & against).Append(CRLF)
        Next
    Catch
'    Log("no vaccination or vaccination parsing error: " & LastException)
    End Try

    Try
        Dim testlist As List = rec1.Get("t")
       
        For Each rec As Map In testlist
            Dim t As Int = rec.Get("tg")
            Dim against As String
            If t = 840539006 Then
                against = "Covid"
            Else
                against = "unknown target"
            End If
           
            Dim r As Int = rec.Get("tr")
            Dim testresult As String
            If r = 260415000 Then
                testresult = "unrecorded"
            Else
                testresult = "unknown"
            End If
            partialparse.Append( "date of test: " & rec.Get("sc") & " where: " & rec.Get("co") & " against: " & against & " result: " & testresult).Append(CRLF)
        Next
    Catch
'        Log("no test or test parsing error: " & LastException)
    End Try

    Try
        Dim recoverylist As List = rec1.Get("r")
       
        For Each rec As Map In recoverylist
            Dim c As Int = rec.Get("tg")
            Dim rfrom As String
            If c = 840539006 Then
                rfrom = "Covid"
            Else
                rfrom = "unknown target"
            End If
           
            partialparse.Append( "recovery from: " & rfrom & " valid until: " & rec.Get("du")).Append(CRLF)
        Next
    Catch
'        Log("no recovery or recovery parsing error: " & LastException)
    End Try

    Log( partialparse.ToString )   ' or do what you want with it
   
End Sub

it's deliberately repetitive and simplistic. it's an example, not exemplary. for those who have trouble with json, it might help in dealing with missing elements.

my understanding is that the validation process requires "public" keys which may not be available to the public... the certificate number which needs to be validated is part of the decoded json string. i didn't include it in the parsing example, but you can see it in the json string. if i can obtain the keys, i'll add a verification method to the library.

by the way, documentation about the certificates is online HERE. don't be fooled by constant references to json. it's not json. that is to say, sometimes it can parse like json, but what it is often breaks a fundamental rule of json: names must be strings. it has to be converted to json or it will break a json parser. otherwise, it's certainly readable as a json-like string. that was really the purpose of the exercise: just another QR code to scan and decode.
i gues the algorithm ia also included or embeded in their online verification. the decoder is in a javascript but unable to extract it because it was obfuscated.
 

drgottjr

Expert
Licensed User
Longtime User

drgottjr

Expert
Licensed User
Longtime User
i gues the algorithm ia also included or embeded in their online verification. the decoder is in a javascript but unable to extract it because it was obfuscated.
if by "algorithm" you mean the public keys, verification is not an issue if you have them. i don't believe the keys are available to anyone who asks for them. i believe you have to be an approved (by the eu) developer. i'm looking to see if this is not the case, but i'm not looking too hard; there are already green pass apps available. this library was for members who had made their own qr code scanning app using other tools available here.

any good qr code reader can read the covid green pass. but what you get requires decryption apart from scanning the qr code. i wanted to see the decrypted result but i didn't want a 3rd party app, so i researched and then built a little library. just plug it into your app after you scan the qr code. the simple parser was an afterthought. if somebody wanted to turn that into a nice class, that would be fine.
 

drgottjr

Expert
Licensed User
Longtime User
attached please find an add-on for your app. it replaces the simple parsing snippet. it's now a class module which also includes decoding the french sanipass (different from the eu's green pass). i'm still working on some of the fields. the french sanipass comes in 2 forms: a qr code and a data matrix code. the class can deal with either one. as with the green pass, there
are multiple generations (versions). i've tried to follow the specs for each one. there is a so-called "binary" version of the sanipass. i found the specification vague as to how it's used in practice.

also attached is a code snippet that helps decide which parser to use after the qr (or data matrix) code has been read.

i've stumbled across a trove of sample codes from dozens of countries. working my way through them. in addition to a qr code, ireland also uses aztec. no problems reported.
 

Attachments

  • govid_addons.zip
    2.1 KB · Views: 201
Last edited:
Top