hc1_verifier

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2021 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// TrustURL is the provider of trusted certificates
	TrustURL = "https://raw.githubusercontent.com/section42/hcert-trustlist-mirror/main/trustlist_de.min.json"
	// ContextSignature is the context field of SigStructure while creating the digest of the COSE
	ContextSignature = "Signature1"
	// ExpirationTrustedCertificates is the expiration constant (12 Hours) to indicate that trusted certificates should be fetched again
	ExpirationTrustedCertificates = 3600 * 12
)
View Source
const (
	HealthCertificatePrefix = "HC1:"
	ZlibMagicNumber         = 0x78
)

Variables

View Source
var AvailableAlgorithms = map[int]*cose.Algorithm{
	-7:  cose.ES256,
	-35: cose.ES384,
	-36: cose.ES512,
}

AvailableAlgorithms The algorithms defined in this document can be found in Table 1.

+=======+=======+=========+==================+
| Name  | Value | Hash    | Description      |
+=======+=======+=========+==================+
| ES256 | -7    | SHA-256 | ECDSA w/ SHA-256 |
+-------+-------+---------+------------------+
| ES384 | -35   | SHA-384 | ECDSA w/ SHA-384 |
+-------+-------+---------+------------------+
| ES512 | -36   | SHA-512 | ECDSA w/ SHA-512 |
+-------+-------+---------+------------------+

       Table 1: ECDSA Algorithm Values
View Source
var TrustedCertificates = TrustedCertificatesStruct{
	Certificates: []TrustURLCertificate{},
	Timestamp:    0,
}

TrustedCertificates to keep the public certificates in the memory

Functions

func CreateCertificateFromPEM

func CreateCertificateFromPEM(data string) (*x509.Certificate, error)

CreateCertificateFromPEM simply creates x509.Certificate from the PEM string TESTCTX -> CERTIFICATE field of a given test file

func CurrentTimestamp added in v0.1.1

func CurrentTimestamp() int64

CurrentTimestamp simply returns the unix time (Number of seconds elapsed since 1 Jan 1970)

func EpochToDateString added in v0.1.1

func EpochToDateString(epoch int64) string

EpochToDateString simply returns the RFC3339 format of date

func ValidateAndRemovePrefix

func ValidateAndRemovePrefix(in string) (string, error)

ValidateAndRemovePrefix simply checks if QR code has a prefix "HC1:" and returns trimmed qr code (without prefix)

func ZlibDecompress

func ZlibDecompress(input []byte) ([]byte, error)

ZlibDecompress simply decompressed the input and returns the decompressed []byte

Types

type CWTClaims

type CWTClaims struct {
	Issuer            string            `cbor:"1,keyasint" json:"iss"`
	ExpirationTime    int64             `cbor:"4,keyasint" json:"exp"`
	IssuedAt          int64             `cbor:"6,keyasint" json:"iat"`
	HealthCertificate HealthCertificate `cbor:"-260,keyasint" json:"hcert"`
}

CWTClaims

3.3 CWT CWTClaims
  3.3.1 CWT Structure Overview
    • Protected	Header
    • Signature	Algorithm (alg, label 1)
    • Key Identifier (kid, label 4)
    • Payload
    • Issuer (iss, claim key 1, optional, ISO 3166-1 alpha-2 of issuer)
    • IssuedAt	(iat, claim	key	6)
    • ExpirationTime (exp, claim key 4)
    • HealthCertificate (hcert, claim key -260)
    – EU Digital Green Certificate v1 (eu_dgc_v1, claim	key	1)
    • Signature

type CovidCertificate

type CovidCertificate struct {
	Version         string           `cbor:"ver" json:"ver"`
	PersonalName    Name             `cbor:"nam" json:"nam"`
	DateOfBirth     string           `cbor:"dob" json:"dob"`
	VaccineRecords  []VaccineRecord  `cbor:"v" json:"v,omitempty"`
	TestRecords     []TestRecord     `cbor:"t" json:"t,omitempty"`
	RecoveryRecords []RecoveryRecord `cbor:"r" json:"r,omitempty"`
}

CovidCertificate https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf

type DGC

type DGC struct {
	V      SignedCWT
	P      SigningHeader
	Claims CWTClaims
	Cert   *x509.Certificate
}

DGC The general structure to keep SignedCWT, SigningHeader, CWTClaims, Cert (will be assigned after verification)

func DecodeCOSE

func DecodeCOSE(coseData []byte) (*DGC, error)

DecodeCOSE simply decodes the coseData and returns DGC without CERT field. The Cert field will be populated once the DGC is verified

func Verify

func Verify(qrCode string) (*DGC, error)

Verify applies the following algorithm to create verified DGC

  1. Prefix validation
  2. Base45 decoding
  3. ZLIB decompression
  4. Decoding the CBOR data
  5. Verification with KID against the trusted list database - Extracting the KID and Issuer (country code) - Searching the KID and country code in the trusted list database - Creating digest of the certificate using the algorithm of the certificate - Verifying the certificate using its digest and signature

func VerifyAPI added in v0.1.3

func VerifyAPI(qrCode string) (*DGC, bool, error)

VerifyAPI applies the following algorithm to create verified DGC. Returns the DGC always if the 4th phase is passed with verification flag and error if exists

  1. Prefix validation
  2. Base45 decoding
  3. ZLIB decompression
  4. Decoding the CBOR data
  5. Verification with KID against the trusted list database - Extracting the KID and Issuer (country code) - Searching the KID and country code in the trusted list database - Creating digest of the certificate using the algorithm of the certificate - Verifying the certificate using its digest and signature

func VerifyWithCertificate

func VerifyWithCertificate(qrCode string, certificate *x509.Certificate) (*DGC, error)

VerifyWithCertificate applies the following algorithm to create verified DGC

  1. Prefix validation
  2. Base45 decoding
  3. ZLIB decompression
  4. Decoding the CBOR data
  5. Verification with provided certificate parameter - Creating digest of the certificate using the algorithm of the certificate - Verifying the certificate using its digest and signature

func (*DGC) CreateSigStructure

func (d *DGC) CreateSigStructure() ([]byte, error)

CreateSigStructure creates the sig structure to be signed by the algorithm hasher 4.4. Signing and Verification Process

In order to create a signature, a well-defined byte stream is needed.
The Sig_structure is used to create the canonical form.  This signing
and verification process takes in the body information (COSE_Sign or
COSE_Sign1), the signer information (COSE_Signature), and the
application data (external source).  A Sig_structure is a CBOR array.
The fields of the Sig_structure in order are:

1.  A text string identifying the context of the signature.  The
    context string is:

       "Signature" for signatures using the COSE_Signature structure.
       "Signature1" for signatures using the COSE_Sign1 structure.
       "CounterSignature" for signatures used as counter signature
       attributes.

2.  The protected attributes from the body structure encoded in a
    bstr type.  If there are no protected attributes, a bstr of
    length zero is used.

3.  The protected attributes from the signer structure encoded in a
    bstr type.  If there are no protected attributes, a bstr of
    length zero is used.  This field is omitted for the COSE_Sign1
    signature structure.
4.  The protected attributes from the application encoded in a bstr
    type.  If this field is not supplied, it defaults to a zero-
    length binary string.  (See Section 4.3 for application guidance
    on constructing this field.)

5.  The payload to be signed encoded in a bstr type.  The payload is
    placed here independent of how it is transported.

The CDDL fragment that describes the above text is:

Sig_structure = [
    context : "Signature" / "Signature1" / "CounterSignature",
    body_protected : empty_or_serialized_map,
    ? sign_protected : empty_or_serialized_map,
    external_aad : bstr,
    payload : bstr
]

func (*DGC) GetAlgorithm

func (d *DGC) GetAlgorithm() (*cose.Algorithm, error)

GetAlgorithm retrieves the algorithm from the protected structure if not found tries to retrieve it from the unprotected structure and checks the algorithm in the AvailableAlgorithms to return *cose.Algorithm

func (*DGC) GetCertificate

func (d *DGC) GetCertificate(countryCode string, keyIdentifier []byte) (*x509.Certificate, error)

GetCertificate tries to find the *x509.Certificate of certificate from the trusted list using the country code (Issuer) and the KID (key identifier)

func (*DGC) GetDigest

func (d *DGC) GetDigest(toBeSigned []byte, algorithm *cose.Algorithm) ([]byte, error)

GetDigest creates the digest with provided toToSigned data and algorithm

func (*DGC) GetKeyIdentifier

func (d *DGC) GetKeyIdentifier() ([]byte, error)

GetKeyIdentifier retrieves the key identifier from the protected structure if not found tries to retrieve it from the unprotected structure

func (*DGC) ToJSONClaims

func (d *DGC) ToJSONClaims() (string, error)

ToJSONClaims simply returns well indented json string of DGC.Claims

func (*DGC) ToJSONHealthCertificate

func (d *DGC) ToJSONHealthCertificate() (string, error)

ToJSONHealthCertificate simply returns well indented json string of the DGC.Claims.HealthCertificate.DigitalGreenCertificate

func (*DGC) Verify

func (d *DGC) Verify() (bool, error)

Verify verifies the certificate against its kid and country code (Issuer) from the trusted urls. if there is a match the public key is returned and the certificate is verified with its digest and signature

func (*DGC) VerifyWithCertificate

func (d *DGC) VerifyWithCertificate(certificate *x509.Certificate) (bool, error)

VerifyWithCertificate test files' key identifiers mostly does not exist in the trusted lists. They are provided with their own certificates. This function verifies a test certificate with provided certificate

type HealthCertificate

type HealthCertificate struct {
	DigitalGreenCertificate CovidCertificate `cbor:"1,keyasint" json:"eu_dgc_v1"`
}

HealthCertificate simply keeps the DGC which is CovidCertificate

type Name

type Name struct {
	FamilyName    string `cbor:"fn" json:"fn"`
	FamilyNameStd string `cbor:"fnt" json:"fnt"`
	GivenName     string `cbor:"gn" json:"gn"`
	GivenNameStd  string `cbor:"gnt" json:"gnt"`
}

Name https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf Section: Person name and date of birth: Person name is the official full name of the person, matching the name stated on travel documents. The identifier of the structure is nam. Exactly 1 (one) person name MUST be provided.

type RecoveryRecord

type RecoveryRecord struct {
	Target            string `cbor:"tg" json:"tg"`
	Country           string `cbor:"co" json:"co"`
	Issuer            string `cbor:"is" json:"is"`
	FirstPositiveTest string `cbor:"fr" json:"fr"`
	ValidFrom         string `cbor:"df" json:"df"`
	ValidUntil        string `cbor:"du" json:"du"`
	CertificateID     string `cbor:"ci" json:"ci"`
}

RecoveryRecord https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf Section: Recovery certificate: Recovery group, if present, MUST contain exactly 1 (one) entry describing exactly one recovery statement. All elements of the recovery group are mandatory, empty values are not supported.

type SignedCWT

type SignedCWT struct {
	Protected   []byte
	Unprotected map[interface{}]interface{}
	Payload     []byte
	Signature   []byte
	// contains filtered or unexported fields
}

SignedCWT https://ec.europa.eu/health/sites/default/files/ehealth/docs/digital-green-certificates_v3_en.pdf 2.6.1 COSE Structure

A COSE structure contains a protected, unprotected and payload object within one CBOR array defined in the Basic Structure of the RFC81527

  Name        CBOR Major Type      Type
  Protected   2                    bstr
  Payload     2                    bstr
  Signature   2                    bstr
  Unprotected 2                    empty

type SigningHeader

type SigningHeader struct {
	Algorithm      int    `cbor:"1,keyasint,omitempty"`
	KeyIdentifiers []byte `cbor:"4,keyasint,omitempty"`
}

SigningHeader 2.6.2 Signing Header

The header of COSE contains the used algorithm and the key identifier:

Name    CBOR Major Type    Placement In Header    Type    Value                             Description
alg     1                  protected              nint    -7/-37 (ES256)                    Algorithm Field
kid     4                  protected              array   First 8 bytes of the hash value   KeyIdentifiers Field

type TestRecord

type TestRecord struct {
	Target         string `cbor:"tg" json:"tg"`
	TestType       string `cbor:"tt" json:"tt"`
	Name           string `cbor:"nm" json:"nm"`
	Manufacturer   string `cbor:"ma" json:"ma"`
	SampleDatetime string `cbor:"sc" json:"sc"`
	TestResult     string `cbor:"tr" json:"tr"`
	TestingCentre  string `cbor:"tc" json:"tc"`
	Country        string `cbor:"co" json:"co"`
	Issuer         string `cbor:"is" json:"is"`
	CertificateID  string `cbor:"ci" json:"ci"`
}

TestRecord https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf Section: Test certificate: Test group, if present, MUST contain exactly 1 (one) entry describing exactly one test result.

type TrustURLCertificate

type TrustURLCertificate struct {
	CertificateType string `json:"certificateType"`
	Country         string `json:"country"`
	KeyIdentifier   string `json:"kid"`
	RawData         string `json:"rawData"`
	Signature       string `json:"signature"`
	ThumbPrint      string `json:"thumbprint"`
	Timestamp       string `json:"timestamp"`
}

TrustURLCertificate is a single trust certificate having country, kid and certificate data fields

func (*TrustURLCertificate) GetCertificate

func (c *TrustURLCertificate) GetCertificate() (*x509.Certificate, error)

GetCertificate returns the x509.Certificate of the current trusted certificate using the RawData field

type TrustURLResponse

type TrustURLResponse struct {
	Certificates []TrustURLCertificate `json:"certificates"`
}

TrustURLResponse is the initial JSON struct returned from the trusted list GET request

type TrustedCertificatesStruct added in v0.1.3

type TrustedCertificatesStruct struct {
	Certificates []TrustURLCertificate
	Timestamp    int64
}

TrustedCertificatesStruct is a struct to keep the trusted certificates response of the TrustURL in the memory to speed up the generation public certificate with an expiration stamp since the data is not changing frequently

func (TrustedCertificatesStruct) HasExpired added in v0.1.3

func (t TrustedCertificatesStruct) HasExpired() bool

HasExpired returns true either there are no certificates or timestamp has already expired

type VaccineRecord

type VaccineRecord struct {
	Target        string  `cbor:"tg" json:"tg"`
	Vaccine       string  `cbor:"vp" json:"vp"`
	Product       string  `cbor:"mp" json:"mp"`
	Manufacturer  string  `cbor:"ma" json:"ma"`
	Doses         float64 `cbor:"dn" json:"dn"`
	DoseSeries    float64 `cbor:"sd" json:"sd"`
	Date          string  `cbor:"dt" json:"dt"`
	Country       string  `cbor:"co" json:"co"`
	Issuer        string  `cbor:"is" json:"is"`
	CertificateID string  `cbor:"ci" json:"ci"`
}

VaccineRecord https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf Section: Certificate type specific information - Vaccination certificate: Vaccination group, if present, MUST contain exactly 1 (one) entry describing exactly one vaccination event. All elements of the vaccination group are mandatory, empty values are not supported.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL