smart-on-fhir / health-cards

Health Cards Framework: implementation guide and supporting material
Other
257 stars 83 forks source link

Clarification re multiple QR codes #178

Open littleforest opened 2 years ago

littleforest commented 2 years ago

I have followed the spec for how to handle the case when the JWS needs to be split up into multiple chunks to create multiple QR codes, and have run the resulting numeric and QR code PNGs through the SMART Health Cards Dev Tools validator successfully. However, when I try to read any of these QR codes with the SMART Health Card Verifier iOS app, I just get an error that it is an unsupported QR code. Are multiple QR codes not yet supported on these apps, or is there some way that they need to be displayed so that they get picked up correctly by the reader? I am having no trouble with single QR codes getting read correctly by the app.

jmandel commented 2 years ago

@jpp9 or @jdkizer9 may be able to comment on this?

jasonxylee commented 2 years ago

Commenting on this as I have also ran into this issue.

According to the SMART Health card specs - it says that Any JWS longer than 1195 characters SHALL be split into "chunks" of length 1191 or smaller;

To implement this logic, I followed the code in the repo at index.ts L179. The result I get is an array of 2 qr code segments that look like this:

[
    { data: 'shc:/1/2/', mode: 'byte' },
    {
      data: '567629095...cut for brevity',
      mode: 'numeric'
    }
  ],
  [
    { data: 'shc:/2/2/', mode: 'byte' },
    {
      data: '4152740303077303... cut for brevity',
      mode: 'numeric'
    }
  ]

However, when using the verifier portal to try to validate the qr code data, the the qr numeric data produces an invalid JWS string. This is shown in the screenshot below. (Using mock data of course)

Screen Shot 2021-09-10 at 10 29 06 AM

I fear that this error is the reason why the validator apps are failing when there are multiple qr codes, because the qr codes that have the numeric data embedded in them don't produce a valid JWS string, making it hard for a node library like node-jose to correctly decode the data and verify.

I would love some guidance on how we should go about fixing this. Perhaps there was a different logic to take for chunking the JWS string once it's longer than 1195 characters in the JWS string.

Appreciate the time, thank you.

ljoy913 commented 2 years ago

@jasonxylee The shc data in the image above (shc:/5676...) is a single-part QR code. The header should be shc:/1/2/5676.... for part-1 of the 2 part code.

Are you encoding the array of 2 parts into a single part instead of encoding each part individually?

The invalid JWS error is the result of the jws missing the signature segment at the end.

jasonxylee commented 2 years ago

@ljoy913 Thank you for your response, the verifier portal does not accept shc/1/2 in the beginning as you can see here:

Screen Shot 2021-09-10 at 11 45 14 AM

Can you please clarify what you mean when you say

"Are you encoding the array of 2 parts into a single part instead of encoding each part individually?"

dleve123 commented 2 years ago

@jasonxylee It appears that you're missing a ~second~ trailing / to denote the N number of numerically-encoded QR chunks that compose your SHC JWS.

Example from the docs:

in a longer JWS, the second chunk in a set of three might produce a QR code like shc:/2/3/56762909524320603460292437404460

Note the shc:/2/3/... component of this.

jasonxylee commented 2 years ago

@dleve123 - Thank you, I made sure that I included a trailing / to denote the N number of numerically-encoded QR Chunks. Please see example below:

Screen Shot 2021-09-10 at 12 08 05 PM

ljoy913 commented 2 years ago

@jasonxylee could you send me the entire shc string from your image?

jasonxylee commented 2 years ago

@ljoy913 Yes, i am including both 1/2, and 2/2 for the numeric qr code:

qrCodeSegments:  [
  [
    { data: 'shc:/1/2/', mode: 'byte' },
    {
      data: '5676290952432060346029243740446031222959532654603460292540772804336028702864716745222809286367633725110556251231413959243862294052654537416441393864407554645528456145365265417239235971386441573762257534412460573601104558125312707424285350387022726060772960720466520834633633704038336663112421394132762263203838547232245072111238626453286311101077744462274111765372275850275432004054681261420012084170086559776272530473346353414506696338101173557176620573415335655745003077692224714572264157636065595669085229546732717664532255443340121150715670726909530928770635687108336773401125711039083223115759205229566904352623300509070408041045722566717267346112630907362930057345567043040853230829444035370568057673094004664254606741676372523625061012333138046368272638715433417060691157617007404376032007396555523633670324336921100921263821090450422003256100726923206656270407285058107174356674360510622532303303672327316606764206597766064438396568123562212726744355656532572559554563591265084324754507552311723105377654655375505811501239335531774034426226307776400471722310572035316242770544005874652352712741440053273106635270703509622323657237505434233007335531312027652759412271352173311011327604550711',
      mode: 'numeric'
    }
  ],
  [
    { data: 'shc:/2/2/', mode: 'byte' },
    {
      data: '0674670505726763057164534252207754096856605506717307053053234371036100325456767362415205665564652530226624122339002269321003684530522907652227243833710304625810656640310612623642387343527509364331385642124063370556110839422770363238065763735812642441387623390826250005370734773636324158085968617528614568566568063977350572563309223057115665243456100334723839281140345624003907283754243443102834402634275028105050742854583836760434222723506225402634275030382832543556402636070755327558702145232956222222391140282064447443207059627321212029723241350036082638112462326307284421327558702145232956222222391140282064447443207059627321212029723425202328453174383676436125231062580707402032596273212923295622262039324431204236764358585862502522202964322574312845317436362253615936207626525007285655627321212029723425202328453174383676435859582477262274256232630728282935753658224561215422762638112424206407754050452129561139326406754050441229551139326405754050440829551139326404754050440429551139326403754050447529552439255659091150014063427539570650587730582869717262213564666245422208330539403008035034115671414177034264052142643057335362276074390063735711763426034230306650253161085600571152200033534374',
      mode: 'numeric'
    }
  ]
]
ljoy913 commented 2 years ago
"Are you encoding the array of 2 parts into a single part instead of encoding each part individually?"

Your shc header for part one should be shc:/1/2/\ for part 1-of-2 Since it was a single part header: shc:/\, something is wrong with the encoding process. I was asking if you are passing the array of two parts to your QR encoder and getting a single QR in return - or - making two calls to the encoder, one for each part, resulting in a QR code for each part?

The verifier cannot validate a single part of a multi-part QR code.
The data from each part is concatenated together to form a single valid JWS string and so all parts are required to continue the validation. It would be like trying to validate the first half of an xml file.

ljoy913 commented 2 years ago

@jasonxylee And you're are trying to generate 2 QR codes from this data? or just validate the data is valid with the tools?

jasonxylee commented 2 years ago

@ljoy913 Thank you for the explanation, i see now that we weren't supposed to separate them out..

We are using the 2 numeric qr codes right now to generate 2 separate QR codes. But I guess it cannot be done that way if it cannot be verified separately.

So then my question now is, once the JWS string is over 1195 characters, and we chunk it into multiple chunks, do we merge all the chunks together to create one QR code? My understanding from reading the SMART Health card documentation was that each chunk represented one QR code, however I guess that could have been a mistake.

ljoy913 commented 2 years ago

@jasonxylee 2 separate QR codes is correct.

You just cannot verify them one at a time in the verifier portal.
If you scan each with the scanner, it will create two entries in the UI and verification will understand an validate them as one. For example (3-part encoding):

image

There is a limitation with the UI that you can only manually enter a single set of numeric data if you are not using the scanner.

Did you try scanning both of your resulting QR codes in the verifier?

littleforest commented 2 years ago

2 separate QR codes is correct.

@ljoy913 could you please comment on how these separate QR codes should be read in the SMART Health Card apps? As I noted in the original question, I have run the 2 QR codes through the SMART Health Cards Dev Tools validator like so:

node . --path ../cr_access/tmp/qrcode_0.png  --path ../cr_access/tmp/qrcode_1.png --type qr

and received a successful validation message (other than the warning that I didn't need to split into 2, which I just did for testing):

Validation results ``` SMART Health Card Dev Tools v1.0.2-0 QR images (2) │ ├─ Debug │ · segment 1: type: byte, content: shc:/1/2/ │ · segment 2: type: numeric, content: 5676295953265460346029254077280433602870286471674522280928631264532675413762453938051241546229775565412733622528382763214061400656627006380645713263676840033604446204593427546031222909524320603460292437404460573601594529536112327424284350386441566212373456700653206230200326662522122830415459042968056727405600213266685008057503034069712275414143407675085035653262126254093764597612100403126576504268030456696061330537675056324145776459251105452964067467766441563157580943085660305368233352552226673037537100386468056868250472766674325871390675291210283543433506606572574127073266572211257107040832373707317535755329117458261045124160340423670611755303032755636337255966663534386803125950750524610853526610375774316866685464606620412352277709326538335868607507247674206409395743296745295575602321302838033832662843052335346034385804265669403521747368446656226056203300 │ · qrcode_0.png = shc:/1/2/5676295953265460346029254077280433602870286471674522280928631264532675413762453938051241546229775565412733622528382763214061400656627006380645713263676840033604446204593427546031222909524320603460292437404460573601594529536112327424284350386441566212373456700653206230200326662522122830415459042968056727405600213266685008057503034069712275414143407675085035653262126254093764597612100403126576504268030456696061330537675056324145776459251105452964067467766441563157580943085660305368233352552226673037537100386468056868250472766674325871390675291210283543433506606572574127073266572211257107040832373707317535755329117458261045124160340423670611755303032755636337255966663534386803125950750524610853526610375774316866685464606620412352277709326538335868607507247674206409395743296745295575602321302838033832662843052335346034385804265669403521747368446656226056203300 │ · segment 1: type: byte, content: shc:/2/2/ │ · segment 2: type: numeric, content: 5841397457110032522958614340670461235061563326577027563620434077753143370704421024035744384506503357523874104531646334264361263505672050057328120661073531602053356611682122255309523967596460381200702230626676103238353454751254226239062261772425360854223365216876373575053706382037065559100308424323213710417261230532285045770804700864310760442331373843553931570363082238103744705527262675756461762060036463667563257267290338296842777630660973563755523165767444223331676356616341350443051109563772072264637553771026692462357161414237744434602738395228040373562975600575057400046755664053232923335060687307545770777158395069732329684170594159546977310034725912082129424171505653003577243900502036012141571020096032615477715758616166085758423144356643553039615210055935410406555275413968107326332145590322440665557641422260706167273608216625533242097709723829274167315774 │ · qrcode_1.png = shc:/2/2/5841397457110032522958614340670461235061563326577027563620434077753143370704421024035744384506503357523874104531646334264361263505672050057328120661073531602053356611682122255309523967596460381200702230626676103238353454751254226239062261772425360854223365216876373575053706382037065559100308424323213710417261230532285045770804700864310760442331373843553931570363082238103744705527262675756461762060036463667563257267290338296842777630660973563755523165767444223331676356616341350443051109563772072264637553771026692462357161414237744434602738395228040373562975600575057400046755664053232923335060687307545770777158395069732329684170594159546977310034725912082129424171505653003577243900502036012141571020096032615477715758616166085758423144356643553039615210055935410406555275413968107326332145590322440665557641422260706167273608216625533242097709723829274167315774 │ ├─ Info │ · qrcode_0.png decoded │ · qrcode_1.png decoded │ └─ QR numeric (2) │ ├─ Debug │ · shc:/1/2/... = eyJhbGciOiJFUzI1NiIsImtpZCI6Il9mbGxVRkZTS29VckJzdnVHNkFISHlBUjU3eks3S3ZtMlpqU0Q1Yk1hOHciLCJ6aXAiOiJERUYifQ.hZJbj9MwEIX_SmVek9ROes3bAkKA0GoFC9IKVch1Jq2pHUe-BMoq_52x00UrtCxVVXUyx5_PnMk9kc6Rmhy97109ny_Wq01erijN2Rp_eMVZzmhF82ZJm3wpymVeLfg6X5eiKbqDNadCGpKRbt-Smq2qqF1uyowMgtT3xJ97IPXXP3inufVH4MofC8Ft415MRR4LxPxbJ8wgG7Z9ViO1Dp38xb00HdllRFhooPOSq09h_x2Ej5bao7RfwLqoqcmioAVDaHz6MnSNgqix4EywAm6TfXJpZJdxiDBKIS0SMoIX2DPOiOSg1GerUPBwvqYoeCieAN- │ · shc:/2/2/... = gVTwf8-MaJgjXUp1jD_jeNGfsHeQAXUzxLXR41W7E0fYSZ3_NfaSw7ZLmlOGXjGP2pA_2vI93j4PLiAbPo8qBCFb6aTphmiS9-sCKkoy7MSPOcx9cCkT3CjzEFQ5cCNnBqyRPx2R3SAR3dh705WXDBR7VujD2MI_Zz51s5mL4iYDLRSXdTLf0l5CS7RYsdHGGxxmjyAi0mloxlFupJ0SJqWzyKo6veRdaLnywYCNLplejlVP1X286eRu4Cmlxbz7GrEkPtjVWRwYOiHSTaI10veJxi2x2w-1pdoUbDJDN_iqv4cfsztgT_rvDJqVshVhcrzL-Ouh95BJWVt_eb-PzET-_AQ.BVf7A6iMjcztfgjjo5fgWLYPoXdKTja72hPV13daxVTq7vGNBZh0CY3ndyVWCisjpHQ5BoFbMW6z6uSJHVpLfw │ · JWS = eyJhbGciOiJFUzI1NiIsImtpZCI6Il9mbGxVRkZTS29VckJzdnVHNkFISHlBUjU3eks3S3ZtMlpqU0Q1Yk1hOHciLCJ6aXAiOiJERUYifQ.hZJbj9MwEIX_SmVek9ROes3bAkKA0GoFC9IKVch1Jq2pHUe-BMoq_52x00UrtCxVVXUyx5_PnMk9kc6Rmhy97109ny_Wq01erijN2Rp_eMVZzmhF82ZJm3wpymVeLfg6X5eiKbqDNadCGpKRbt-Smq2qqF1uyowMgtT3xJ97IPXXP3inufVH4MofC8Ft415MRR4LxPxbJ8wgG7Z9ViO1Dp38xb00HdllRFhooPOSq09h_x2Ej5bao7RfwLqoqcmioAVDaHz6MnSNgqix4EywAm6TfXJpZJdxiDBKIS0SMoIX2DPOiOSg1GerUPBwvqYoeCieAN-gVTwf8-MaJgjXUp1jD_jeNGfsHeQAXUzxLXR41W7E0fYSZ3_NfaSw7ZLmlOGXjGP2pA_2vI93j4PLiAbPo8qBCFb6aTphmiS9-sCKkoy7MSPOcx9cCkT3CjzEFQ5cCNnBqyRPx2R3SAR3dh705WXDBR7VujD2MI_Zz51s5mL4iYDLRSXdTLf0l5CS7RYsdHGGxxmjyAi0mloxlFupJ0SJqWzyKo6veRdaLnywYCNLplejlVP1X286eRu4Cmlxbz7GrEkPtjVWRwYOiHSTaI10veJxi2x2w-1pdoUbDJDN_iqv4cfsztgT_rvDJqVshVhcrzL-Ouh95BJWVt_eb-PzET-_AQ.BVf7A6iMjcztfgjjo5fgWLYPoXdKTja72hPV13daxVTq7vGNBZh0CY3ndyVWCisjpHQ5BoFbMW6z6uSJHVpLfw │ ├─ Info │ · shc:/1/2/... decoded │ · shc:/2/2/... decoded │ · All shc parts decoded │ ├─ Warning │ · JWS of size 868 (<= 1191) didn't need to be split in 2 chunks │ └─ JWS-compact │ ├─ Debug │ · JWS.header = {"alg":"ES256","kid":"_fllUFFSKoUrBsvuG6AHHyAR57zK7Kvm2ZjSD5bMa8w","zip":"DEF"} │ · JWS.signature = 0557fb03a88c8dcced7e08e3a397e058b60fa1774a4e36bbda13d5d7775ac554eaeef18d059874098de77725560a2b23a4743906815b316eb3eae4891d5a4b7f │ · Downloaded issuer key(s) : │ · Key 0: │ · { │ "crv": "P-256", │ "kty": "EC", │ "x": "t7t42SeAjfW_qcZg2QOvKw2FwV6Sk-Nj1ere40ScO8I", │ "y": "5BV0yKzhwSq9_Hni69yiBq-ATizUdFHjAzHmv8vWjiA", │ "kid": "_fllUFFSKoUrBsvuG6AHHyAR57zK7Kvm2ZjSD5bMa8w", │ "use": "sig", │ "alg": "ES256" │ } │ ├─ Info │ · JWS payload inflated │ · Retrieving issuer key from https://4768-2600-1700-a3a1-1030-d50d-5c25-34a7-72cd.ngrok.io/.well-known/jwks.json │ · Validating key : key[_fllUFFSKoUrBsvuG6AHHyAR57zK7Kvm2ZjSD5bMa8w] │ · JWS signature verified │ └─ JWS.payload │ ├─ Debug │ · JWS Payload Contents: │ · { │ "iss": "https://4768-2600-1700-a3a1-1030-d50d-5c25-34a7-72cd.ngrok.io", │ "nbf": 1631030582, │ "vc": { │ "type": [ │ "https://smarthealth.cards#health-card", │ "https://smarthealth.cards#covid19", │ "https://smarthealth.cards#immunization" │ ], │ "credentialSubject": { │ "fhirVersion": "4.0.1", │ "fhirBundle": { │ "resourceType": "Bundle", │ "type": "collection", │ "entry": [ │ { │ "fullUrl": "resource:0", │ "resource": { │ "resourceType": "Patient", │ "name": [ { "family": "Peabody", "given": [ "Henry" ] } ], │ "birthDate": "1950-01-01" │ } │ }, │ { │ "fullUrl": "resource:1", │ "resource": { │ "resourceType": "Immunization", │ "meta": { "security": [ { "code": "IAL1.2" } ] }, │ "status": "completed", │ "vaccineCode": { "coding": [ { "system": "http://hl7.org/fhir/sid/cvx", "code": "208" } ] }, │ "patient": { "reference": "resource:0" }, │ "occurrenceDateTime": "2021-08-31", │ "manufacturer": { │ "identifier": { "system": "http://hl7.org/fhir/sid/mvx", "value": "PFR" } │ }, │ "performer": [ │ { │ "actor": { "display": "1 Park Avenue, 1 Park Avenue, New York, NY, 10016" } │ } │ ], │ "lotNumber": "123_J9" │ } │ } │ ] │ } │ } │ } │ } │ ├─ Info │ · JWS Payload validated │ └─ FhirBundle │ ├─ Debug │ · FHIR Bundle Contents: │ · { │ "resourceType": "Bundle", │ "type": "collection", │ "entry": [ │ { │ "fullUrl": "resource:0", │ "resource": { │ "resourceType": "Patient", │ "name": [ { "family": "Peabody", "given": [ "Henry" ] } ], │ "birthDate": "1950-01-01" │ } │ }, │ { │ "fullUrl": "resource:1", │ "resource": { │ "resourceType": "Immunization", │ "meta": { "security": [ { "code": "IAL1.2" } ] }, │ "status": "completed", │ "vaccineCode": { "coding": [ { "system": "http://hl7.org/fhir/sid/cvx", "code": "208" } ] }, │ "patient": { "reference": "resource:0" }, │ "occurrenceDateTime": "2021-08-31", │ "manufacturer": { "identifier": { "system": "http://hl7.org/fhir/sid/mvx", "value": "PFR" } }, │ "performer": [ { "actor": { "display": "1 Park Avenue, 1 Park Avenue, New York, NY, 10016" } } ], │ "lotNumber": "123_J9" │ } │ } │ ] │ } │ └─ Info · FHIR bundle validated Validation completed ```

But I get errors when scanning the QR codes with the app readers.

ljoy913 commented 2 years ago

@littleforest Sorry, I am not the person that can help with that app, but in out verifier portal You would click 'Scan code' to bring up the scanner. Scan the one of the qr codes. (you can scan out of order) After the first scan, it will detect it as being multi-part and wait to scan the 2nd part. Scan the 2nd code, and it should close the scanner and the verification should continue.

littleforest commented 2 years ago

My 2 QR codes worked fine in the verifier portal. I'm guessing the issue must be that the current app readers out there do not have the capability to read multiple QR codes? I can close this ticket if that is likely the case. Thanks!