rust-italia/dgc

A parser and validator for the EU Digital Green Certificate (dgc) a

It offers a minimal and easy to use API

dgc

A parser and validator for the EU Digital Green Certificate (dgc) a.k.a. greenpass 📲✅

  • Parses the text content of a European Digital Green Certificate (dgc or greenpass) and extract the embedded data
  • Uses a Trustlist of public keys and Elliptic Curve cryptography to be able to validate the signature of a given certificate
  • It's tested against 500+ certificates from the official testing dataset
  • It offers a minimal and easy to use API
  • The certificate data can be easily serialized/deserialized for ease of testing and reporting
  • It embeds the official valueset so that internal IDs (diseases, result types, countries, testing authorities, etc.) can be easily expanded to their descriptive equivalents
  • It reports errors for all fallible operations minimising the opportunity for panicking
  • Offers utilities for easily populate a Trustlist from various types of keys and apis

Usage

To install the latest version of dgc, add this to your Cargo.toml:

[dependencies]
dgc = "*"

Dgc in action

This library tries to address 2 main use cases:

1. Decode a certificate without validating its signature

let raw_certificate_data = "HC1:NCF:603A0T9WTWGSLKC 4K694WJN.0J$6C-7WAB0XK3JCSGA2F3R8PP4V2F35VPP.EY50.FK8ZKO/EZKEZ96LF6/A6..DV%DZJC0/D5UA QELPCG/DYUCHY83UAGVC*JCNF6F463W5KF6VF6IECSHG4KCD3DX47B46IL6646H*6MWEWJDA6A:961A6Q47EM6B$DFOC0R63KCZPCNF6OF63W5$Q6+96/SA5R6NF61G73564KC*KETF6A46.96646B565WEC.D1$CKWEDZC6VCS446$C4WEUPC3JCUIA+ED$.EF$DMWE8$CBJEMVCB445$CBWER.CGPC4WEOPCE8FHZA1+9LZAZM81G72A62+8OG7J09U47AB8V59T%6ZHBO57X48RUIY03XQOK*FZUNM UFY4D5C S3R9UW-2R*4KZJT5M MIM:03RMZNA LKTO34PA.H51966PS0KAP-KLPH.Q6$KSTJ0-G658RL5HR1";
let certificate_container = dgc::decode(raw_certificate_data).expect("Cannot parse certificate data");
println!("{:#?}", certificate_container);

2. Decode a certificate and validate the signature against a trustlist

let raw_certificate_data = "HC1:NCF:603A0T9WTWGSLKC 4K694WJN.0J$6C-7WAB0XK3JCSGA2F3R8PP4V2F35VPP.EY50.FK8ZKO/EZKEZ96LF6/A6..DV%DZJC0/D5UA QELPCG/DYUCHY83UAGVC*JCNF6F463W5KF6VF6IECSHG4KCD3DX47B46IL6646H*6MWEWJDA6A:961A6Q47EM6B$DFOC0R63KCZPCNF6OF63W5$Q6+96/SA5R6NF61G73564KC*KETF6A46.96646B565WEC.D1$CKWEDZC6VCS446$C4WEUPC3JCUIA+ED$.EF$DMWE8$CBJEMVCB445$CBWER.CGPC4WEOPCE8FHZA1+9LZAZM81G72A62+8OG7J09U47AB8V59T%6ZHBO57X48RUIY03XQOK*FZUNM UFY4D5C S3R9UW-2R*4KZJT5M MIM:03RMZNA LKTO34PA.H51966PS0KAP-KLPH.Q6$KSTJ0-G658RL5HR1";
// This is a X509 certificate that contains a Public Key
let signature_certificate = "MIIDujCCAaKgAwIBAgIIKUgZWBL1pnMwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCRlIxHTAbBgNVBAoTFElNUFJJTUVSSUUgTkFUSU9OQUxFMR4wHAYDVQQLExVGT1IgVEVTVCBQVVJQT1NFIE9OTFkxGDAWBgNVBAMTD0lOR1JPVVBFIERTYyBDQTAeFw0yMTA2MDIxMjE0MDBaFw0yMTA5MDIxMjE0MDBaMEAxCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDRVJUSUdOQTEeMBwGA1UEAwwVQ0VSVElHTkEgLSBURVNUIERHQyAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETdygPqv/l6tWFqHFEIEZxfdhtbrBpDgVjmUN4CKOu/EQFwkVVQ/4N0BamwtI0hSnSZP72byk6XqpMErYWRTCbKNdMFswCQYDVR0TBAIwADAdBgNVHQ4EFgQUUjXs7mCY2ZgROQSsw1CN0qM4Zj8wHwYDVR0jBBgwFoAUYLoYTllzE2jOy3VMAuU4OJjOingwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAvxuSBWNOrk+FRIbU42tnwZBllUeNH7cWcrYHV0O+1k3RbpvYa0YE2J0du301/a+0+pqlatR8o8Coe/NFt4/KSu+To+i8uZXiHJn2XrAZgwPqqTvsMUVwFPWhwJpLMCejmU0A8JEhXH7s0BN6orqIH0JKLpl0/MdVviIUksnxPnP2wdCtz6dL5zKhi+Qt8BFr55PL1dvuWxnuFOsKr89MqaexQVe/WvKhG5GXBaJFDbp4USVX9Z8vwp4SfEs5nh0ti0M2fyGrpfPvWWFra/qoRGAUJEPHHPMqZT45c1rXo12+cpme2CYM4rsliQsaqdH462p7YNNI5reBC+WHhzGr9FGq9yZ1gu/yhz1cJxNwE5gsBTWnJmSnRE75lYj1a/GAb+9wfABd1Vx68Fnww3Ngp8lG2T1vEWhwQusj/OmloVbqjJiCi6PcZ1/OSTbx58Zv9ySwDd3QGxPygfMy87FuhT6iWlPv57qTMrgtEjq89J8v3WnReAhp12ru5ehN2Zv0ZkO1Of0H3yxNBsvfHUgpgwsRn4zjLVbkU+a3hr4famOThmB1X0tuikY0mbNtVejPGS0qCgeLgj8ILlUrRtsW4R6WzZdIsz7H9AYnpyZbdMPsa856xBR9s0+AzguJI9kkJxvVcpR//GiXMhs0EdgWj2rouOEPZiFNdWpVRrxv/kw==";

// We create a new Trustlist (container of "trusted" public keys)
let mut trustlist = dgc::TrustList::default();
// We add the public key in the certificate to the trustlist
trustlist
    .add_key_from_certificate(signature_certificate)
    .expect("Failed to add key from certificate");

// Now we can validate the signature (this returns)
let (certificate_container, signature_validity) =
    dgc::validate(raw_certificate_data, &trustlist).expect("Cannot parse certificate data");

println!("{:#?}", &certificate_container);

// Checks the validity of the signature
match signature_validity {
    dgc::SignatureValidity::Valid => println!("The certificate signature is Valid!"),
    e => println!("Could not validate the signature: {}", e),
}

Other examples?

To get started using dgc, see the examples or the docs.

If you clone the repository locally, you can easily run the example files with:

cargo run --example <name of example file>

Italian support

Do you need support for Italian validation rules? Check dgc-italy-core!

Data structure

The information is stored inside a certification in a multi-layered format.

This library tries to find a compromise between making the information as accessible as possible and respecting the original structure of the raw data.

The following diagram represents how the information is organised once a certificate has been decoded:

FAQ

Is it legal to use this library?

You can certainly use this library to look into your own personal DGC (or the ones provided in the official test dataset).

If you are trying to use this library to look into certificates of arbitrarty individuals, you need to be aware that you will have access to privacy-sensitive personal information. Privacy-related regulation might limit you or prevent you from using this library (or the data acquired through this library). It is recommended to consult the relevant legal sources and authorities for any significant production use case.

Note that this software is licensed under MIT license and it is provided "as is". The authors of this library take no responsibility on any issue (especially legal ones) that might arise from using this library or the data acquired through it.

Contributing

Everyone is very welcome to contribute to this project. You can contribute just by submitting bugs or suggesting improvements by opening an issue on GitHub.

License

Licensed under MIT License. © Luciano Mammino + Rust Italia.

Issues

Collection of the latest Issues

lu-zero

lu-zero

enhancement
Comment Icon3
  • Make sure the current API is set (pending @dodomorandi rework)
  • Write a capi guarded module.
  • Add cbindgen.conf with the minimal features
  • Document how to install it in the README.md
dodomorandi

dodomorandi

testing
Comment Icon1

There are a few json files that fails for strange reasons.

For instance, as discussed in #33, there are some public keys that use ECDSA-P384 signing algorithm, even if the alg field in the fields indicates a RSA-PSS-SHA256 signature.

Maybe it is something that we cannot really fix, but at least we have a tracking issue to refer.

lmammino

lmammino

enhancement
Comment Icon2

Right now the library only validates a certificate based ONLY on the status of the signature. In reality a certificate can be considered invalid even if the signature is validated correctly.

As far as I understand there are several other factors that we should support in terms of validation:

  • Signature validity: already supported ✅
  • Certificate emission time and expiry time. The DgcContainer struct already collects these timestamps but we offer no easy way to check the current time against them.
  • Business Rules (or country-specific rules): see #19 for dedicated issue.

In the context of this issue I think it will important to figure out an ergonomic API that:

  1. should make it easy to validate the certificate in one single operation (function call)
  2. Return the certificate data (if we can parse that correctly)
  3. Return a clear error in case of validation failed (for instance it's very important to distinguish whether a certificate is expired or whether it doesn't satisfy a specific regional rule)

Maybe we could have a dedicated CertificateValidity struct that can contain various fields like this:

SignatureValidity, TimeValidity and BusinessRulesValidity could be enums that can encapsulate all the different state of validation that is relevant for them. For instance:

Finally we could have a is_valid() method on the CertificateValidity struct that simply returns true or false if all the conditions are satisfied or not...

lmammino

lmammino

enhancement
Comment Icon1

If we aim to get formal approval from Italian authorities as a good library to use for DGC parsing and validation I think it could make sense to implement functionality for easily populating a Trustlist using the Italian APIs.

It seems that the 2 main endpoints for that are:

We could check the code here or here as a starting point

lmammino

lmammino

enhancement
Comment Icon0

Every country publishes country-specific rulesets (also called business rules) that are used to determine if a certificate is valid on a given country based on national rules.

E.g. It's likely that in Italy "negative test certificates" won't be considered valid for most activities.

These rulesets are somewhat documented here: https://github.com/ehn-dcc-development/dgc-business-rules

For Italy you can find the current ruleset at the following URL: https://get.dgc.gov.it/v1/dgc/settings

Interesting enough there is even a black list encode as a rule!

lmammino

lmammino

enhancement
Comment Icon1

First of all, it would be convenient to have a way to distinguish between test, recovery and vaccine data through an enum. This might look like the following code:

Once we have this, DgcContainer could implement a method like get_first_cert() which might work as follows (untested):

A structurally valid Dgc should contain only one entry, so we can use this method to extract that easily.

Versions

Find the latest versions by id

0.0.8 - Dec 09, 2021

What's Changed

Full Changelog: https://github.com/rust-italia/dgc/compare/0.0.7...0.0.8

0.0.7 - Dec 09, 2021

What's Changed

Full Changelog: https://github.com/rust-italia/dgc/compare/0.0.6...0.0.7

0.0.6 - Nov 29, 2021

What's Changed

Full Changelog: https://github.com/rust-italia/dgc/compare/0.0.5...0.0.6

0.0.5 - Nov 27, 2021

What's Changed

New Contributors

Full Changelog: https://github.com/rust-italia/dgc/compare/0.0.4...0.0.5

0.0.4 - Nov 24, 2021

  • Updated links in reame after moving the main repo ownership to rust-italia

0.0.3 - Nov 24, 2021

  • Removed temp dependency ciboriumvalue and switched to official ciborium version 0.2.0. Thanks to @npmccallum (#11)
  • Using CoW to expand values in a more efficient way and get rid of lazy_static. Thanks to @nappa85 (#13)
  • More examples and README updates (#17)
  • Allow floating points in DgcCertContainer. Thanks to @dodomorandi (#16)
  • Remove possible panic on remove_prefix. Thanks to @Rimpampa (#15)

0.0.2 - Nov 20, 2021

  • Minor improvement based on first round of triaging against official test dataset

0.0.1 - Nov 20, 2021

  • First public release (complete but requires better docs and more tests)

Information - Updated Jul 28, 2022

Stars: 26
Forks: 13
Issues: 12

Repositories & Extras

This is an example of a Rust server that functions as a remote schema for...

Rust + Hasura Rust server that functions as a Hasura

This is an example of a Rust server that functions as a remote schema for...

Newport Engine is a modular 2D and 3D game engine built in Rust for Rust

It is designed to be easily extendable and easy to use

Newport Engine is a modular 2D and 3D game engine built in Rust for Rust

Newport Engine is a modular 2D and 3D game engine built in Rust for Rust

It is designed to be easily extendable and easy to use

Newport Engine is a modular 2D and 3D game engine built in Rust for Rust

liboqs-rust: Rust bindings for liboqs

Qyantum Safe liboqs rust bindings

liboqs-rust: Rust bindings for liboqs

msgflo-rust: Rust participant support for MsgFlo

Flowhub visual programming IDE

msgflo-rust: Rust participant support for MsgFlo
Actix

1.0K

How to be a full stack Rust Developer

Read Rust the Rust blog posts at Steadylearner

How to be a full stack Rust Developer

Rust library translation (rust-src/rust-std/stdlib/rustlib translation)

This is the place to translate Having a documentation in your native language is essential if you don't speak English, and still enjoyable even if...

Rust library translation (rust-src/rust-std/stdlib/rustlib translation)

False Positive for rust-lang/rust#83583

The deprecation lint proc_macro_derive_resolution_fallback is intended to catch proc macro generated code that refers to items from parent modules that should not be in scope:

False Positive for rust-lang/rust#83583

A CHIP-8 &amp; SuperChip interpreter written in Rust using rust-sdl2

If you're getting compile errors it may be because

A CHIP-8 &amp; SuperChip interpreter written in Rust using rust-sdl2

Rust-Svelte-on-Rust

Starter template for Rocket backend server

Rust-Svelte-on-Rust

xbuild is a build tool for rust and rust/flutter projects with support for cross compiling...

xbuild is a build tool for rust and rust/flutter projects with support for cross compiling and

xbuild is a build tool for rust and rust/flutter projects with support for cross compiling...
Facebook Instagram Twitter GitHub Dribbble
Privacy