profile
viewpoint

smart-on-fhir/health-cards 199

Health Cards Framework: implementation guide and supporting material

smart-on-fhir/fhir-parser 122

A Python FHIR specification parser and class generator

apple/FHIRModels 106

Swift library for FHIR® resource data models

smart-on-fhir/health-cards-tests 33

Demo for health wallet with Verifiable Credentials and Decentralized Identifiers

carekit-apple/FHIRModels 9

Swift library for FHIR® resource data models

p2-apple/ehn-sign-verify-python-trivial 0

Extremely minimal python implementation of the eHN-S protocol.

p2-apple/fhir-parser 0

A Python FHIR specification parser and class generator

PR closed apple/FHIRModels

Update CodeSystemMedicationStatusCodes.swift

Align the codes with the standard - taken from https://www.hl7.org/fhir/codesystem-medication-statement-status.html

+18 -2

6 comments

1 changed file

jkiddo

pr closed time in 4 days

pull request commentapple/FHIRModels

Update CodeSystemMedicationStatusCodes.swift

Ah of course, thanks @xinsight ! @jkiddo , what you want is https://github.com/apple/FHIRModels/blob/main/Sources/ModelsR4/CodeSystemMedicationStatementStatusCodes.swift

jkiddo

comment created time in 4 days

pull request commentapple/FHIRModels

Update CodeSystemMedicationStatusCodes.swift

Hmmm, I wonder why these are not up to date, though.

jkiddo

comment created time in 5 days

pull request commentapple/FHIRModels

Update CodeSystemMedicationStatusCodes.swift

Thanks for these, Jens, but these are all auto-generated, we can't accept changes individually. I'll re-run generation soon to account for these changes.

jkiddo

comment created time in 5 days

issue commentapple/FHIRModels

Need some help accessing extensions

You are close, and together with @drdavec 's suggestions the solution is something like:

  • Fix stringValue to valueString in your sample JSON
  • Put the utility method on FHIRPrimitive instead
extension FHIRPrimitive {
    
    var mothersFamilyName: String? {
        guard let mothersFamilyExtension = extensions(for: "http://hl7.org/fhir/StructureDefinition/humanname-mothers-family").first else {
            return nil
        }
        if case .string(let str) = mothersFamilyExtension.value {
            return str.value?.string
        }
        // Not a `valueString`
        return nil
    }
}

Running the code:

import ModelsR4

let string = """
    {
        "resourceType": "Patient",
        "id": "2429563849",
        "name": [
            {
                "use": "official",
                "family": "SMITH",
                "_family": {
                    "extension": [
                        {
                            "url": "http://hl7.org/fhir/StructureDefinition/humanname-mothers-family",
                            "valueString": "JOHNSON"
                        }
                    ]
                },
                "given": [
                    "JOHN"
                ]
            }
        ]
    }
    """
guard let data = string.data(using: .utf8) else {
    throw ...
}

let decoder = JSONDecoder()
let patient = try decoder.decode(Patient.self, from: data)
for name in patient.name ?? [] {
    print("family: \(name.family?.value?.string ?? "{nil}"), mother's family: \(name.family?.mothersFamilyName ?? "{nil}")")
}
// prints: "family: SMITH, mother's family: JOHNSON"
patagoniacode

comment created time in 8 days

fork p2-apple/ehn-sign-verify-python-trivial

Extremely minimal python implementation of the eHN-S protocol.

fork in 9 days

issue commentapple/FHIRModels

Need some help accessing extensions

Yes indeed, it's still on my bucket list to improve documentation, as called out in #1 . Does this comment help you get at the extension?

https://github.com/apple/FHIRModels/issues/1#issuecomment-652106332

patagoniacode

comment created time in 11 days

Pull request review commentsmart-on-fhir/health-cards

2nd revocation proposal

 When an issuer generates a new key to sign Health Cards, the public key SHALL be issuer's JWK set in its `jwks.json` file. Retired private keys that are no longer used to sign Health Cards SHALL be destroyed. Older public key entries that are needed to validate previously signed Health Cards SHALL remain in the JWK set for as long as the corresponding Health Cards-are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key-from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key.+are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key; verifiers will from then on reject all Health Cards signed using that key.++### Revocation++Individual Health Cards MAY be revoked using a revocation identifier property `rid` encoded in the `vc` claim of the JWT. This should be a short identifier, meaningless to the verifiers; the only constraint is that the identifier SHALL use the base64url alphabet (but doesn’t need to be base64url encoded). Issuers MAY use application-specific user identifiers for this purpose, but since these could be publicly listed in revocation lists, issuers SHOULD use a one-way transformation of the data combined with enough entropy to prevent reversal. It is RECOMMENDED to use the base64url encoding of the first 64 bits of the output of HMAC-SHA-256 (as specified in [RFC 4868](https://tools.ietf.org/html/rfc4868)) using a 256-bit random secret key concatenated with the `<<kid>>`.

Ok, but if we think 64 bits are enough, which is like ~11 base64 chars, then we can set a much lower limit?

christianpaquin

comment created time in 18 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentsmart-on-fhir/health-cards

2nd revocation proposal

 When an issuer generates a new key to sign Health Cards, the public key SHALL be issuer's JWK set in its `jwks.json` file. Retired private keys that are no longer used to sign Health Cards SHALL be destroyed. Older public key entries that are needed to validate previously signed Health Cards SHALL remain in the JWK set for as long as the corresponding Health Cards-are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key-from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key.+are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key; verifiers will from then on reject all Health Cards signed using that key.++### Revocation++Individual Health Cards MAY be revoked using a revocation identifier property `rid` encoded in the `vc` claim of the JWT. This should be a short identifier, meaningless to the verifiers; the only constraint is that the identifier SHALL use the base64url alphabet (but doesn’t need to be base64url encoded). Issuers MAY use application-specific user identifiers for this purpose, but since these could be publicly listed in revocation lists, issuers SHOULD use a one-way transformation of the data combined with enough entropy to prevent reversal. It is RECOMMENDED to use the base64url encoding of the first 64 bits of the output of HMAC-SHA-256 (as specified in [RFC 4868](https://tools.ietf.org/html/rfc4868)) using a 256-bit random secret key concatenated with the `<<kid>>`.

OTOH should we just specify how to calculate the rid and be done with it? I'm not sure what the risk of conflict is if we just take the first 64 bits.

christianpaquin

comment created time in 18 days

Pull request review commentsmart-on-fhir/health-cards

2nd revocation proposal

 When an issuer generates a new key to sign Health Cards, the public key SHALL be issuer's JWK set in its `jwks.json` file. Retired private keys that are no longer used to sign Health Cards SHALL be destroyed. Older public key entries that are needed to validate previously signed Health Cards SHALL remain in the JWK set for as long as the corresponding Health Cards-are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key-from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key.+are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key; verifiers will from then on reject all Health Cards signed using that key.++### Revocation++Individual Health Cards MAY be revoked using a revocation identifier property `rid` encoded in the `vc` claim of the JWT. This should be a short identifier, meaningless to the verifiers; the only constraint is that the identifier SHALL use the base64url alphabet (but doesn’t need to be base64url encoded). Issuers MAY use application-specific user identifiers for this purpose, but since these could be publicly listed in revocation lists, issuers SHOULD use a one-way transformation of the data combined with enough entropy to prevent reversal. It is RECOMMENDED to use the base64url encoding of the first 64 bits of the output of HMAC-SHA-256 (as specified in [RFC 4868](https://tools.ietf.org/html/rfc4868)) using a 256-bit random secret key concatenated with the `<<kid>>`.

Yeah, agree with the implicit goal. So I presume it only makes sense to have an absolute upper limit. I'm not sure what a good value is, should we aim to allow 256 bits, even though we recommend taking the first 64 only? So, in base64, that's like 43 chars, can we limit to 50 chars? Too much? Too little?

christianpaquin

comment created time in 18 days

PullRequestReviewEvent

Pull request review commentsmart-on-fhir/health-cards

2nd revocation proposal

 When an issuer generates a new key to sign Health Cards, the public key SHALL be issuer's JWK set in its `jwks.json` file. Retired private keys that are no longer used to sign Health Cards SHALL be destroyed. Older public key entries that are needed to validate previously signed Health Cards SHALL remain in the JWK set for as long as the corresponding Health Cards-are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key-from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key.+are clinically relevant. However, if a private signing key is compromised, then the issuer SHALL immediately remove the corresponding public key from the JWK set in its `jwks.json` file and request revocation of all X.509 certificates bound to that public key; verifiers will from then on reject all Health Cards signed using that key.++### Revocation++Individual Health Cards MAY be revoked using a revocation identifier property `rid` encoded in the `vc` claim of the JWT. This should be a short identifier, meaningless to the verifiers; the only constraint is that the identifier SHALL use the base64url alphabet (but doesn’t need to be base64url encoded). Issuers MAY use application-specific user identifiers for this purpose, but since these could be publicly listed in revocation lists, issuers SHOULD use a one-way transformation of the data combined with enough entropy to prevent reversal. It is RECOMMENDED to use the base64url encoding of the first 64 bits of the output of HMAC-SHA-256 (as specified in [RFC 4868](https://tools.ietf.org/html/rfc4868)) using a 256-bit random secret key concatenated with the `<<kid>>`.

Can we impose an absolute upper bound of how long a rid can be?

christianpaquin

comment created time in 18 days

PullRequestReviewEvent

pull request commentStateOfCalifornia/DigitalCovid19VaccineRecord-UI

Provide Apple links on iPhone but not iPad

iPads in mobile mode will have CPU iPhone OS so I think they will actually match. 😬

jmandel

comment created time in 20 days

pull request commentStateOfCalifornia/DigitalCovid19VaccineRecord-UI

Provide Apple links on iPhone but not iPad

userAgent.match("iPhone") will match for all of them, though, because they all have CPU iPhone in the agent.

jmandel

comment created time in 20 days

pull request commentStateOfCalifornia/DigitalCovid19VaccineRecord-UI

Provide Apple links on iPhone but not iPad

The newer iPads will claim they are a Mac, so the user agent will be something like:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15

Users can switch to their "mobile" user agent, which looks something like:

Mozilla/5.0 (iPad; CPU iPhone OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Mobile/15C44 Safari/604.1

And iPod touch will announce itself as something like:

Mozilla/5.0 (iPod; CPU iPhone OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Mobile/15C44 Safari/604.1

And an iPhone for completeness sake:

Mozilla/5.0 (iPhone; CPU iPhone OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Mobile/15C44 Safari/604.1
jmandel

comment created time in 20 days

PullRequestReviewEvent
PullRequestReviewEvent

issue openedthe-commons-project/vci-directory

Invalid URL (trailing space character) for Boston Medical Center

There is a trailing space in the iss entry for Boston Medical Center, which is invalid if strictly parsing that field as URLs.

created time in a month

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentthe-commons-project/vci-directory

Add support for new metadata properties

 def test_invalid_keyset_kid_missing(self):  class ValidateEntriesTestCase(unittest.TestCase): +    maxDiff = None+     def test_validate_entries1(self):         entries = [-            IssuerEntry('SHC Example Issuer 1', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 2', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 3', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 4', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 5', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 6', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 7', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 8', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 9', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 10', 'https://spec.smarthealth.cards/examples/issuer')+            IssuerEntry('SHC Example Issuer 1', 'https://spec.smarthealth.cards/examples/issuer', 'https://smarthealth.cards/', None),+            IssuerEntry('SHC Example Issuer 2', 'https://spec.smarthealth.cards/examples/issuer', 'https://spec.smarthealth.cards/', None),+            IssuerEntry('SHC Example Issuer 3', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 4', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 5', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 6', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 7', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 8', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 9', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 10', 'https://spec.smarthealth.cards/examples/issuer', None, None)         ]          actual = validate_entries(entries)

Ok, no problem, it's just tests. Maybe add that comment?

jdkizer9

comment created time in 2 months

PullRequestReviewEvent

issue commentthe-commons-project/vci-directory

Augment directory with metadata

Do we trust all issuers to issue both vaccinations and lab tests? Do we need the metadata to indicate which VC types https://spec.smarthealth.cards/vocabulary/ the issuer is trusted to issue?

That's still an open question, see my comment above on healthCardTypes.

jmandel

comment created time in 2 months

issue commentthe-commons-project/vci-directory

Augment directory with metadata

I think I would expect your second post, with both iss still present. This prevents "old" clients from breaking but allows to do more once canonical_iss is supported.

jmandel

comment created time in 2 months

Pull request review commentthe-commons-project/vci-directory

Add support for new metadata properties

 def __repr__(self):  NAME_KEY = 'name' ISS_KEY = 'iss'+WEBSITE_KEY = 'website'+CANONICAL_ISS_KEY = 'canonicalIss'

Since we seem to be using participating_issuers, should this also be snake-case canonical_iss?

jdkizer9

comment created time in 2 months

Pull request review commentthe-commons-project/vci-directory

Add support for new metadata properties

 def test_invalid_keyset_kid_missing(self):  class ValidateEntriesTestCase(unittest.TestCase): +    maxDiff = None+     def test_validate_entries1(self):         entries = [-            IssuerEntry('SHC Example Issuer 1', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 2', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 3', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 4', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 5', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 6', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 7', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 8', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 9', 'https://spec.smarthealth.cards/examples/issuer'),-            IssuerEntry('SHC Example Issuer 10', 'https://spec.smarthealth.cards/examples/issuer')+            IssuerEntry('SHC Example Issuer 1', 'https://spec.smarthealth.cards/examples/issuer', 'https://smarthealth.cards/', None),+            IssuerEntry('SHC Example Issuer 2', 'https://spec.smarthealth.cards/examples/issuer', 'https://spec.smarthealth.cards/', None),+            IssuerEntry('SHC Example Issuer 3', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 4', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 5', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 6', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 7', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 8', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 9', 'https://spec.smarthealth.cards/examples/issuer', None, None),+            IssuerEntry('SHC Example Issuer 10', 'https://spec.smarthealth.cards/examples/issuer', None, None)         ]          actual = validate_entries(entries)

I don't fully understand why this validates: all iss are the same, would this not trigger the duplicate error?

jdkizer9

comment created time in 2 months

more