logo

欧盟数字 COVID 证书工具包

一个用于解码、验证和校验欧盟数字 COVID 证书的 Swift 包
适用于 iOS、tvOS、watchOS 和 macOS

Swift 5.4 Documentation Twitter

免责声明

EUDCCKit 并非欧盟数字 COVID 证书的官方实现

特性

安装

Swift 包管理器

要使用 Apple 的 Swift 包管理器进行集成,请将以下内容作为依赖项添加到您的 Package.swift

dependencies: [
    .package(url: "https://github.com/SvenTiigi/EUDCCKit.git", from: "0.0.1")
]

或者导航到您的 Xcode 项目,然后选择 Swift Packages,单击“+”图标并搜索 EUDCCKit

用法

EUDCCKit Swift 包由四个不同的库组成,用于解码、验证和校验欧盟数字 COVID 证书。

EUDCC

EUDCC 库包含欧盟数字 COVID 证书的模型定义

import EUDCC

// The EU Digital COVID Certificate model
let eudcc: EUDCC

// Access content of EUDCC
switch eudcc.content {
case .vaccination(let vaccination):
    print("Vaccination", vaccination)
case .test(let test):
    print("Test", test)
case .recovery(let recovery):
    print("Recovery", recovery)
}

请访问高级部分以了解更多信息。

EUDDCDecoder

EUDCCDecoder 库提供了一个 EUDCCDecoder 对象,该对象能够解码欧盟数字 COVID 证书的 Base-45 字符串表示形式,该形式通常嵌入在二维码中。

import EUDCCDecoder

// Initialize an EUDCCDecoder
let decoder = EUDCCDecoder()

// The Base-45 encoded EU Digital COVID Certificate from a QR-Code
let qrCodeContent = "HC1:..."

// Decode EUDCC from QR-Code
let decodingResult = decoder.decode(from: qrCodeContent)

// Switch on decoding result
switch decodingResult {
case .success(let eudcc):
    // Successfully decoded Digital COVID Certificate
    print("EU Digital COVID Certificate", eudcc)
case .failure(let decodingError):
    // Decoding failed with error
    print("Failed to decode EUDCC", decodingError)
}

请访问高级部分以了解更多信息。

EUDCCVerifier

EUDCCVerifier 库提供了一个 EUDCCVerifier 对象,该对象可用于验证欧盟数字 COVID 证书的加密签名。

import EUDCCVerifier

// Initialize an EUDDCCVerifier
let verifier = EUDCCVerifier(
    trustService: EUCentralEUDCCTrustService()
)

// Verify EU Digital COVID Certificate
verifier.verify(eudcc: eudcc) { verificationResult in
    // Switch on verification result
    switch verificationResult {
    case .success(let trustCertificate):
        print("Cryptographically valid", trustCertificate)
    case .invald:
        print("Invalid EUDCC")
    case .failure(let error):
        print("Error occured during verification", error)
    }
}

请访问高级部分以了解更多信息。

EUDCCValidator

EUDCCValidator 库提供了一个 EUDCCValidator 对象,该对象可用于根据给定的规则校验欧盟数字 COVID 证书。

import EUDCCValidator

// Initialize an EUDCCValidator
let validator = EUDCCValidator()

// Validate EU Digital COVID Certificate
let validationResult = validator.validate(
    eudcc: eudcc,
    rule: .isFullyImmunized() && !.isVaccinationExpired()
)

// Switch on validation result
switch validationResult {
case .success:
    // Successfully validated EU Digital COVID Certificate
    print("Successfully validated")
case .failure(let validationError):
    // Validation failure
    print("Validation failed", validationError)
}

请访问高级部分以了解更多信息。

高级

EUDCC

内容

除了 EUDCCcontent 属性之外,您还可以使用以下便捷属性来检查 EUDCC 是否包含疫苗接种、检测或康复对象。

import EUDCC

// Vaccination
let vaccination: EUDCC.Vaccination? = eudcc.vaccination

// Test
let test: EUDCC.Test? = eudcc.test

// Recovery
let recovery: EUDCC.Recovery? = eudcc.recovery

知名值

以下每个对象都公开了一个 WellKnownValue 枚举,该枚举可用于检索有关特定值的更详细信息

import EUDCC

let vaccineMedicinalProduct: EUDCC.Vaccination.VaccineMedicinalProduct

// Switch on WellKnownValue of VaccineMedicinalProduct
switch vaccineMedicinalProduct.wellKnownValue {
    case .covid19VaccineModerna:
        break
    case .vaxzevria:
        break
    default:
        break
}

编码

EUDCC 包含两个属性 cryptographicSignaturebase45Representation,它们是方便的对象,并非欧盟数字 COVID 证书 JSON 架构的正式组成部分。

如果您希望在编码 EUDCC 时跳过这些属性,您可以将以下 userInfo 配置设置为 JSONEncoder

import EUDCC

let encoder = JSONEncoder()

encoder.userInfo = [
    // Skip encoding CryptographicSignature
    EUDCC.EncoderUserInfoKeys.skipCryptographicSignature: true,
    // Skip encoding Base-45 representation
    EUDCC.EncoderUserInfoKeys.skipBase45Representation: true,
]

let jsonData = try encoder.encode(eudcc)

EUDDCDecoder

解码

EUDCCDecoder 支持解码 Base-45 编码的 StringData 对象。

import EUDCCDecoder

let eudccDecoder = EUDCCDecoder()

// Decode from Base-45 encoded String
let eudccBase45EncodedString: String
let stringDecodingResult = eudccDecoder.decode(
    from: eudccBase45EncodedString
)

// Decode from Base-45 encoded Data
let eudccBase45EncodedData: Data
let dataDecodingResult = eudccDecoder.decode(
    from: eudccBase45EncodedData
)

便捷解码

通过导入 EUDCCDecoder 库,EUDCC 对象将扩展一个静态 decode 函数。

import EUDCCDecoder

let decodingResult = EUDCC.decode(from: "HC1:...")

EUDCCVerifier

EUDCCTrustService

为了验证 EUDCC,需要使用 EUDCCTrustService 的实例来实例化 EUDCCVerifier,该实例用于检索信任证书。

import EUDCC
import EUDCCVerifier

struct SpecificEUDCCTrustService: EUDCCTrustService {
    
    /// Retrieve EUDCC TrustCertificates
    /// - Parameter completion: The completion closure
    func getTrustCertificates(
        completion: @escaping (Result<[EUDCC.TrustCertificate], Error>) -> Void
    ) {
        // TODO: Retrieve TrustCertificates and invoke completion handler
    }
    
}

let eudccVerifier = EUDCCVerifier(
    trustService: SpecificEUDCCTrustService()
)

EUDCCKit 附带了两个预定义的 EUDCCTrustService 实现

如果您希望从多个 EUDCCTrustService 实现中检索证书,则可以使用 GroupableEUDCCTrustService

let trustService = GroupableEUDCCTrustService(
    trustServices: [
        EUCentralEUDCCTrustService(),
        RobertKochInstituteEUDCCTrustService()
    ]
)

trustService.getTrustCertificates { certificates in
    // ...
}

便捷验证

通过导入 EUDCCVerifier 库,EUDCC 对象将扩展一个 verify 函数。

import EUDCC
import EUDCCVerifier

let eudcc: EUDCC

eudcc.verify(
    using: EUDCCVerifier(
        trustService: EUCentralEUDCCTrustService()
    )
) { verificationResult in
    switch verificationResult {
    case .success(let trustCertificate):
        break
    case .invald:
        break
    case .failure(let error):
        break
    }
}

EUDCCValidator

ValidationRule

可以使用 EUDCCValidator 和给定的 EUDCC.ValidationRule 来校验 EUDCCEUDCC.ValidationRule 可以用一个简单的闭包初始化,该闭包接收一个 EUDCC 并返回一个 Bool,指示校验是成功还是失败。

import EUDCC
import EUDCCValidator

// Simple EUDCC ValidationRule instantiation
let validationRule = EUDCC.ValidationRule { eudcc in
    // Process EUDCC and return Bool result
}

// EUDCC ValidationRule with Tag in order to uniquely identify a ValidationRule
let isVaccinationComplete = EUDCC.ValidationRule(
    tag: "is-vaccination-complete"
) { eudcc in
    eudcc.vaccination?.doseNumber == eudcc.vaccination?.totalSeriesOfDoses
}

EUDCCKit 附带了许多预定义的 EUDCC.ValidationRule,例如以下这些。

import EUDCC
import EUDCCValidator

let eudcc: EUDCC
let validator = EUDCCValidator()

// Is fully immunized
validator.validate(
    eudcc: eudcc, 
    rule: .isFullyImmunized(minimumDaysPast: 15)
)

// Is tested positive
validator.validate(
    eudcc: eudcc, 
    rule: .isTestedPositive
)

逻辑/条件运算符

为了创建更复杂的规则,每个 EUDCC.ValidationRule 都可以通过应用标准运算符链接在一起。

import EUDCC
import EUDCCValidator

let defaultValidationRule: EUDCC.ValidationRule = .if(
    .isVaccination,
    then: .isFullyImmunized() && .isWellKnownVaccineMedicinalProduct && !.isVaccinationExpired(),
    else: .if(
        .isTest,
        then: .isTestedNegative && .isTestValid(),
        else: .if(
            .isRecovery,
            then: .isRecoveryValid,
            else: .constant(false)
        )
    )
)

便捷校验

通过导入 EUDCCValidator 库,EUDCC 对象将扩展一个 validate 函数。

import EUDCC
import EUDCCValidator

let eudcc: EUDCC

let validationRule = eudcc.validate(
    rule: .isWellKnownVaccineMedicinalProduct
)

许可证

EUDCCKit
Copyright (c) 2021 Sven Tiigi sven.tiigi@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.