SlideShare una empresa de Scribd logo
1 de 70
Descargar para leer sin conexión
A look inside the
European Covid Green Certificate
Luciano Mammino ( )
@loige
loige.link/rust-green
22-02-2022 😱
1
Get these slides!
loige
loige.link/rust-green
2
👋I'm Luciano
Senior Architect @ fourTheorem (Dublin )
nodejsdp.link
📔Co-Author of Node.js Design Patterns  👉
Let's connect!
  (blog)
  (twitter)
  (twitch)
  (github)
loige.co
@loige
loige
lmammino
3
We are business focused
technologists that deliver.
 |  |
Accelerated Serverless AI as a Service Platform Modernisation
We are hiring: do you want to
?
work with
us
loige 4
loige
🦀I'm learning Rust as a hobby...
Live streaming my attempts to crack Advent of Code in Rust (with
and ): /
@gbinside @88_eugen Twitch YouTube
Wrote articles:
a few
How to to_string in Rust
Rust shenanigans: return type polymorphism
Where to go to learn Rust in 2021
Published (simple) crates: , , ,
a few jwtinfo allwords gmaps-static dgc
5
Disclaimers
loige
🤓I am not involved with the DGC working group
 
😢COVID has been tough on everyone,
      we'll try to focus only on the tech here!
6
Agenda + Goals
loige
1. Needs and principles
2. 🗝Cryptographic model
3. 📦The data
4. 🧅Layers of encoding
5. 🛠Decoding in Rust
🤨Learn some cool technologies
🧐Learn a tiny bit of Rust
🤓Be nerdy and have fun!
7
The need for a digital
certificate in the COVID age
loige 8
The need for a digital
certificate in the COVID age
loige
😷We need a system to quickly provide a proof against COVID
      (Vaccination, negative test, proof of recovery) 
It needs to be personal, easy to carry around (digital),
       easy to issue and to validate
🌎It needs to be secure against forgery and work across countries
9
The EU Covid Green Pass
a.k.a. 
Electronic Health
Certificates (HCERT)
 
loige.link/hcert-spec
loige 10
Electronic Health Certificates (HCERT)
Requirements & Guiding Principles
loige
✍Signed data with machine readable
content
📃Use compact encoding
🤲Based on open standards
11
Asymmetric cryptographic signatures
loige
🤫 Private Key 📢 Public Key
🔗
101010101000101010010... 0101010101010101010101...
12
Asymmetric cryptographic signatures
loige
🤫 Private Key 📢 Public Key
101010101000101010010... 0101010101010101010101...
The owner of the
private key signs the
document
Anyone can validate the
signature using the
public key
13
What's inside a certificate?
loige
DGC container
Cryptographic header (Key Id,
Algorithm)
Cryptographic Signature
Header (Issuer, Issue date, expiry date)
14
Certificates list
vaccine, test, or recovery data
Personal data (name, surname, DoB)
An
example
loige loige.link/green-examples 15
{
"1": "DK",
"4": 1625054000,
"6": 1622462000,
"-260": {
"ver":"1.0.0",
"nam":{
"fn":"Klaus",
"fnt":"KLAUS",
"gn":"Jørgensen",
"gnt":"JOERGENSEN"
},
"dob":"1983-01-06",
"v":[
{
"tg":"840539006",
"vp":"1119349007",
"mp":"EU/1/20/1528",
"ma":"ORG-100030215",
"dn":2,
"sd":2,
"dt":"2021-05-03",
"co":"DK",
"is":"Danish Health Data Authority",
"ci":"URN:UVCI:01:DK:B986830007345F99AE898FB82C6C61F2#A"
}
]
}
}
An
example
loige loige.link/green-examples
Personal info
Vaccine info
DGC Header
16
Layered
encoding
loige
QRCODE ASCII mode
Prefix + Base45 encoding
Zlib compression
CWT signed data (COSE)
CBOR document
17
🧅
loige
HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-36
5KN-TMLV4.P7*ZOP-IMJTI94F/8X*G3M9JUPY0BZW4Z
*AK.GNNVR*G0C7PHBO335KN/NBEDBVBJ623323EAJ7U
J5PNDIB6PNS7B1DN%BBWC7WC7GB3683ML7SZ4ZI00T9
UKPSH9WC5PF6846A$Q 76QW6A/98T5WBI$E9$UPV3Q.
GUQ$9WC5R7ACB97C968ELZ5$DP6PP5IL*PP:+P*.1D9
R8Q02-DE%QHOJ+PB/VSQOL9DLKWCZ3EBKDYGIZ J$XI
4OIMEDTJCJKDLEDL9CZTAKBI/8D:8DKTDL+SQ05.$S6
ZC0JBY63-C3F+LBQ99Q9E$BDZIA9JJ-JS7BYZJ92KG0
TB9FNDA5KD9FED.B4JB3E9B9NNPCV9E6LFSD9C8J-QD
SWNG4C-TLNKE$JDVPLW1KD0KCZGBKQCJE%RH5WAMSSR
$F-75NXONQ84QV9/7/-LT1AIYBZGD$9RCLV-PTZ-K63
ET-D1757H3GF9MV2N7WNQSY1SBZT-:81JJLHFQ-VG$H
K00XWPD2
QRCode content "HC1:" (prefix)
Binary data encoded in
Base45
18
Base45
loige 19
loige
Allows to encode binary data in text format (ASCII)
Like Base64, but it uses 45 characters instead:
loige.link/base45-explained
Base45
20
Base45
loige
01001001 00100000 01100111
01101111 01110100 00100000
01101101 01111001 00100000
01110011 01101000 01101111
01110100 01110011 00100000
11011000 00111101
UTF8 (17 bytes)
I got my shots 💉
Hex (38 bytes)
49 20 67 6f 74 20 6d 79 20 73 68
6f 74 73 20 f0 9f 92 89
Base64 (28 bytes)
SSBnb3QgbXkgc2hvdHMg8J+SiQ==
Base45 (29 bytes)
0B9J3DSUEZ$DR4459DLWEH74Z7K23
Some binary data
21
loige
loige.link/base45-rfc
"A QR-code is used to encode text as a graphical image. [...] QR-
codes cannot be used to encode arbitrary binary data directly.  [...]
Compared to already established Base64, Base32 and Base16
encoding schemes [...], the Base45 scheme described in this
document offer a more compact QR-code encoding"
Base45
22
loige
Base45
NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-
TMLV4.P7*ZOP-IMJTI94F/8X*G3M9JUPY0BZW4Z*AK.
GNNVR*G0C7PHBO335KN/NBEDBVBJ623323EAJ7UJ5PN
DIB6PNS7B1DN%BBWC7WC7GB3683ML7SZ4ZI00T9UKPS
H9WC5PF6846A$Q 76QW6A/98T5WBI$E9$UPV3Q.GUQ$
9WC5R7ACB97C968ELZ5$DP6PP5IL*PP:+P*.1D9R8Q0
2-DE%QHOJ+PB/VSQOL9DLKWCZ3EBKDYGIZ J$XI4OIM
EDTJCJKDLEDL9CZTAKBI/8D:8DKTDL+SQ05.$S6ZC0J
BY63-C3F+LBQ99Q9E$BDZIA9JJ-JS7BYZJ92KG0TB9F
NDA5KD9FED.B4JB3E9B9NNPCV9E6LFSD9C8J-QDSWNG
4C-TLNKE$JDVPLW1KD0KCZGBKQCJE%RH5WAMSSR$F-7
5NXONQ84QV9/7/-LT1AIYBZGD$9RCLV-PTZ-K63ET-D
1757H3GF9MV2N7WNQSY1SBZT-:81JJLHFQ-VG$HK00X
WPD2
Base45
Decode
Compressed binary data
23
Zlib compression
loige 24
Zlib compression
loige
"zlib is designed to be a free, general-purpose, legally
unencumbered -- that is, not covered by any patents --
lossless data-compression library for use on virtually any
computer hardware and operating system"
zlib.net
25
Zlib compression
loige
Zlib inflate
Compressed binary data Decompressed binary data
26
Zlib compression
loige
👀We start to see
some "readable"
information!
27
COSE / CWT
loige
CBOR Object Signing and Encryption / CBOR Web Token
28
CBOR !? 🤔
loige
But wait, what the heck is
✋
29
CBOR
loige
TLDR; Like JSON but in
binary!
loige.link/cbor-rfc
30
JSON
loige
A schema-less data format where a value can be:
Null
Boolean
Number
String
Array
Object
null
true
-17.34
"A programmer walks into a bar..."
["foo", 1.23, null, false, [22]]
{"foo": "bar", "manyvals": [1,2,3], "nested": {}}
31
CBOR
loige
A schema-less binary data format where a value can be:
Null
Boolean
Number
String Text
Array
Object Map
F6
F5
fbc031570a3d70a3d7
7820412070726f6772616d6d65722077616c6b7320696e746f2061206261722e2e2e
8563666f6ffb3ff3ae147ae147aef6f48116
a363666f6f63626172686d616e7976616c7383010203666e6573746564a0
32
CBOR
loige
It also supports:
Byte String (a sequence of raw bytes)
Tags (annotations that allow to create new types)
33
CBOR
loige
An example:
A3 63666F6F 63626172 686D616E7976616C73 83 01 02 03 666E6573746564 A0
{
"foo": "bar",
"manyvals": [
}
],
"nested": {}
1, 2, 3
34
COSE / CWT
loige
CBOR Object Signing and Encryption / CBOR Web Token
ok... again 😅
35
COSE
loige
loige.link/cose-rfc
CBOR Object Signing and Encryption
COSE (inspired by ) defines CBOR-based protocols for:
JOSE
Encrypted data
Cryptographic signed data
MACed data
36
CWT
loige
Like but for CBOR
JWT
loige.link/cwt-rfc
Defines a protocol for transferring claims between parties
CBOR Web Token
Claims are digitally signed for authenticity
37
CWT
loige
A CWT is made of 4 parts:
Protected header
CBOR Web Token
Non protected header
Payload
Signature
38
CWT
loige
A CWT is encoded as a (tagged) CBOR array with 4 values:
Protected header (binary string)
CBOR Web Token
Non protected header (map)
Payload (binary string)
Signature (binary string)
39
Protected
header
Binary String
loige
Array (length
4)
CWT tag
40
Unprotected
header
Map (length 0)
Payload
Binary String
Signature
Binary String
loige
CWT payload
{
"1": "DK",
"4": 1625054000,
"6": 1622462000,
"-260": {
"ver":"1.0.0",
"nam":{
"fn":"Klaus",
"fnt":"KLAUS",
"gn":"Jørgensen",
"gnt":"JOERGENSEN"
},
"dob":"1983-01-06",
"v":[
{
"tg":"840539006",
"vp":"1119349007",
"mp":"EU/1/20/1528",
"ma":"ORG-100030215",
"dn":2,
"sd":2,
"dt":"2021-05-03",
"co":"DK",
"is":"Danish Health Data Authority",
"ci":"URN:UVCI:01:DK:B986830007345F99AE898FB82C6C61F2#A"
}
]
}
}
Binary String follows (CBOR Encoded)
CBOR decode
    (to JSON)
41
How to decode - quick recap
loige
1. Remove "HC1:" prefix
2. Base45 decode
3. Zlib decompress
4. Parse CWT
5. Parse CWT Payload as CBOR
6. Party hard! 🥳
Hey, let's implement
this...
in Rust!
42
loige
cargo new dgc-decode
Project bootstrap
43
// src/main.rs
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
todo!()
// 1. Remove "HC1:" prefix
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
1
2
3
4
5
6
7
8
9
10
11
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
// src/main.rs
1
2
fn main() {
3
4
todo!()
5
// 1. Remove "HC1:" prefix
6
// 2. Base45 decode
7
// 3. Zlib decompress
8
// 4. Parse CWT
9
// 5. Parse CWT Payload as CBOR
10
}
11
todo!()
// 1. Remove "HC1:" prefix
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
// src/main.rs
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
5
6
7
8
9
10
}
11
// src/main.rs
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
todo!()
// 1. Remove "HC1:" prefix
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
1
2
3
4
5
6
7
8
9
10
11
loige 44
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
todo!();
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn remove_prefix(data: &str) -> &str {
todo!()
// remove "HC1:" prefix and return remaining string
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let no_prefix = remove_prefix(cert_data);
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
3
todo!();
4
// 2. Base45 decode
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn remove_prefix(data: &str) -> &str {
11
todo!()
12
// remove "HC1:" prefix and return remaining string
13
}
14
fn remove_prefix(data: &str) -> &str {
todo!()
// remove "HC1:" prefix and return remaining string
}
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
todo!();
4
// 2. Base45 decode
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
11
12
13
14
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
todo!();
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn remove_prefix(data: &str) -> &str {
todo!()
// remove "HC1:" prefix and return remaining string
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
loige 45
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
todo!();
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn remove_prefix(data: &str) -> &str {
if data.len() < 4 || !data.starts_with("HC1:") {
panic!("Invalid prefix"); // IRL use a Result!
}
&data[4..]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if data.len() < 4 || !data.starts_with("HC1:") {
panic!("Invalid prefix"); // IRL use a Result!
}
&data[4..]
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
todo!();
4
// 2. Base45 decode
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn remove_prefix(data: &str) -> &str {
11
12
13
14
15
16
}
17
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
todo!();
// 2. Base45 decode
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn remove_prefix(data: &str) -> &str {
if data.len() < 4 || !data.starts_with("HC1:") {
panic!("Invalid prefix"); // IRL use a Result!
}
&data[4..]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loige 46
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
todo!()
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decode_base45(data: &str) -> Vec<u8> {
todo!()
// parse the string as base45 encoded and return the
// resulting raw bytes
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let decoded = decode_base45(no_prefix);
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
4
todo!()
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn decode_base45(data: &str) -> Vec<u8> {
11
todo!()
12
// parse the string as base45 encoded and return the
13
// resulting raw bytes
14
}
15
16
// ...
17
fn decode_base45(data: &str) -> Vec<u8> {
todo!()
// parse the string as base45 encoded and return the
// resulting raw bytes
}
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
todo!()
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
11
12
13
14
15
16
// ...
17
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
todo!()
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decode_base45(data: &str) -> Vec<u8> {
todo!()
// parse the string as base45 encoded and return the
// resulting raw bytes
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loige 47
loige
cargo add base45
# Cargo.toml
# ...
[dependencies]
base45 = "3.0.0"
48
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
todo!()
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decode_base45(data: &str) -> Vec<u8> {
base45::decode(data).unwrap() // IRL use a Result!
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
base45::decode(data).unwrap() // IRL use a Result!
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
todo!()
5
// 3. Zlib decompress
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn decode_base45(data: &str) -> Vec<u8> {
11
12
}
13
14
// ...
15
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
todo!()
// 3. Zlib decompress
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decode_base45(data: &str) -> Vec<u8> {
base45::decode(data).unwrap() // IRL use a Result!
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
loige 49
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
todo!()
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decompress(data: Vec<u8>) -> Vec<u8> {
todo!()
// decompress using zlib inflate
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let decompressed = decompress(decoded);
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
5
todo!()
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn decompress(data: Vec<u8>) -> Vec<u8> {
11
todo!()
12
// decompress using zlib inflate
13
}
14
15
// ...
16
fn decompress(data: Vec<u8>) -> Vec<u8> {
todo!()
// decompress using zlib inflate
}
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
let decompressed = decompress(decoded);
5
todo!()
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
11
12
13
14
15
// ...
16
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
todo!()
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decompress(data: Vec<u8>) -> Vec<u8> {
todo!()
// decompress using zlib inflate
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
loige 50
loige
cargo add inflate
# Cargo.toml
# ...
[dependencies]
base45 = "3.0.0"
inflate = "0.4.5"
51
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
todo!()
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decompress(data: Vec<u8>) -> Vec<u8> {
inflate::inflate_bytes_zlib(data.as_slice()).unwrap()
// IRL use a Result!
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
inflate::inflate_bytes_zlib(data.as_slice()).unwrap()
// IRL use a Result!
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
let decompressed = decompress(decoded);
5
todo!()
6
// 4. Parse CWT
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn decompress(data: Vec<u8>) -> Vec<u8> {
11
12
13
}
14
15
// ...
16
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
todo!()
// 4. Parse CWT
// 5. Parse CWT Payload as CBOR
}
fn decompress(data: Vec<u8>) -> Vec<u8> {
inflate::inflate_bytes_zlib(data.as_slice()).unwrap()
// IRL use a Result!
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
loige 52
Are we on the right track?
loige
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
println!("{}", String::from_utf8_lossy(&decompressed));
}
// ...
1
2
3
4
5
6
7
8
9
10
println!("{}", String::from_utf8_lossy(&decompressed));
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
let decompressed = decompress(decoded);
5
6
7
}
8
9
// ...
10
53
Are we on the right track?
loige
We are starting to see some
readable info! 🤩 54
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
todo!()
// 5. Parse CWT Payload as CBOR
}
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
todo!()
// Parse raw bytes as CBOR.
// Extract and return the raw bytes representing
// the CWT payload
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let cwt_payload = get_cwt_payload(decompressed);
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
let decompressed = decompress(decoded);
5
6
todo!()
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
11
todo!()
12
// Parse raw bytes as CBOR.
13
// Extract and return the raw bytes representing
14
// the CWT payload
15
}
16
17
// ...
18
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
todo!()
// Parse raw bytes as CBOR.
// Extract and return the raw bytes representing
// the CWT payload
}
fn main() {
1
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
2
let no_prefix = remove_prefix(cert_data);
3
let decoded = decode_base45(no_prefix);
4
let decompressed = decompress(decoded);
5
let cwt_payload = get_cwt_payload(decompressed);
6
todo!()
7
// 5. Parse CWT Payload as CBOR
8
}
9
10
11
12
13
14
15
16
17
// ...
18
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
todo!()
// 5. Parse CWT Payload as CBOR
}
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
todo!()
// Parse raw bytes as CBOR.
// Extract and return the raw bytes representing
// the CWT payload
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
loige 55
loige
cargo add ciborium
# Cargo.toml
# ...
[dependencies]
base45 = "3.0.0"
ciborium = "0.2.0"
inflate = "0.4.5"
56
use ciborium::{de::from_reader, value::Value};
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
todo!()
// 5. Parse CWT Payload as CBOR
}
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
let parsed: Value = from_reader(data.as_slice()).unwrap();
println!("{:?}", parsed);
todo!()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
todo!()
9
// 5. Parse CWT Payload as CBOR
10
}
11
12
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
13
let parsed: Value = from_reader(data.as_slice()).unwrap();
14
println!("{:?}", parsed);
15
todo!()
16
}
17
18
// ...
19
let parsed: Value = from_reader(data.as_slice()).unwrap();
println!("{:?}", parsed);
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
todo!()
9
// 5. Parse CWT Payload as CBOR
10
}
11
12
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
13
14
15
todo!()
16
}
17
18
// ...
19
loige 57
58
loige
// ...
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
// IRL avoid .unwrap() like hell and propagate errors correctly!
let parsed: Value = from_reader(data.as_slice()).unwrap();
let (tag, arr) = parsed.as_tag().unwrap();
assert_eq!(tag, 18);
let arr = arr.as_array().unwrap();
let payload = arr[2].as_bytes().unwrap();
payload.clone()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
let parsed: Value = from_reader(data.as_slice()).unwrap();
// ...
1
2
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
3
// IRL avoid .unwrap() like hell and propagate errors correctly!
4
5
let (tag, arr) = parsed.as_tag().unwrap();
6
assert_eq!(tag, 18);
7
let arr = arr.as_array().unwrap();
8
let payload = arr[2].as_bytes().unwrap();
9
payload.clone()
10
}
11
12
// ...
13
let (tag, arr) = parsed.as_tag().unwrap();
assert_eq!(tag, 18);
// ...
1
2
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
3
// IRL avoid .unwrap() like hell and propagate errors correctly!
4
let parsed: Value = from_reader(data.as_slice()).unwrap();
5
6
7
let arr = arr.as_array().unwrap();
8
let payload = arr[2].as_bytes().unwrap();
9
payload.clone()
10
}
11
12
// ...
13
let arr = arr.as_array().unwrap();
// ...
1
2
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
3
// IRL avoid .unwrap() like hell and propagate errors correctly!
4
let parsed: Value = from_reader(data.as_slice()).unwrap();
5
let (tag, arr) = parsed.as_tag().unwrap();
6
assert_eq!(tag, 18);
7
8
let payload = arr[2].as_bytes().unwrap();
9
payload.clone()
10
}
11
12
// ...
13
let payload = arr[2].as_bytes().unwrap();
payload.clone()
// ...
1
2
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
3
// IRL avoid .unwrap() like hell and propagate errors correctly!
4
let parsed: Value = from_reader(data.as_slice()).unwrap();
5
let (tag, arr) = parsed.as_tag().unwrap();
6
assert_eq!(tag, 18);
7
let arr = arr.as_array().unwrap();
8
9
10
}
11
12
// ...
13
// ...
fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> {
// IRL avoid .unwrap() like hell and propagate errors correctly!
let parsed: Value = from_reader(data.as_slice()).unwrap();
let (tag, arr) = parsed.as_tag().unwrap();
assert_eq!(tag, 18);
let arr = arr.as_array().unwrap();
let payload = arr[2].as_bytes().unwrap();
payload.clone()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
loige 59
use ciborium::{de::from_reader, value::Value};
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
let parsed_payload = parse_cwt_payload(cwt_payload);
}
fn parse_cwt_payload(data: Vec<u8>) -> Value {
// parse the binary data as CBOR
todo!()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let parsed_payload = parse_cwt_payload(cwt_payload);
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
9
}
10
11
fn parse_cwt_payload(data: Vec<u8>) -> Value {
12
// parse the binary data as CBOR
13
todo!()
14
}
15
16
// ...
17
fn parse_cwt_payload(data: Vec<u8>) -> Value {
// parse the binary data as CBOR
todo!()
}
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
let parsed_payload = parse_cwt_payload(cwt_payload);
9
}
10
11
12
13
14
15
16
// ...
17
loige 60
use ciborium::{de::from_reader, value::Value};
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
let parsed_payload = parse_cwt_payload(cwt_payload);
println!("{:#?}", parsed_payload);
}
fn parse_cwt_payload(data: Vec<u8>) -> Value {
from_reader(data.as_slice()).unwrap()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from_reader(data.as_slice()).unwrap()
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
let parsed_payload = parse_cwt_payload(cwt_payload);
9
println!("{:#?}", parsed_payload);
10
}
11
12
fn parse_cwt_payload(data: Vec<u8>) -> Value {
13
14
}
15
16
// ...
17
println!("{:#?}", parsed_payload);
use ciborium::{de::from_reader, value::Value};
1
2
fn main() {
3
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
4
let no_prefix = remove_prefix(cert_data);
5
let decoded = decode_base45(no_prefix);
6
let decompressed = decompress(decoded);
7
let cwt_payload = get_cwt_payload(decompressed);
8
let parsed_payload = parse_cwt_payload(cwt_payload);
9
10
}
11
12
fn parse_cwt_payload(data: Vec<u8>) -> Value {
13
from_reader(data.as_slice()).unwrap()
14
}
15
16
// ...
17
use ciborium::{de::from_reader, value::Value};
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
let parsed_payload = parse_cwt_payload(cwt_payload);
println!("{:#?}", parsed_payload);
}
fn parse_cwt_payload(data: Vec<u8>) -> Value {
from_reader(data.as_slice()).unwrap()
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loige 61
loige 62
Ok, let's make it more readable... 😅
loige
cargo add serde_json
63
use ciborium::{de::from_reader, value::Value};
use serde_json::to_string_pretty;
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
let parsed_payload = parse_cwt_payload(cwt_payload);
println!("{}", to_string_pretty(&parsed_payload).unwrap());
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use serde_json::to_string_pretty;
use ciborium::{de::from_reader, value::Value};
1
2
3
fn main() {
4
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
5
let no_prefix = remove_prefix(cert_data);
6
let decoded = decode_base45(no_prefix);
7
let decompressed = decompress(decoded);
8
let cwt_payload = get_cwt_payload(decompressed);
9
let parsed_payload = parse_cwt_payload(cwt_payload);
10
println!("{}", to_string_pretty(&parsed_payload).unwrap());
11
}
12
13
// ...
14
println!("{}", to_string_pretty(&parsed_payload).unwrap());
use ciborium::{de::from_reader, value::Value};
1
use serde_json::to_string_pretty;
2
3
fn main() {
4
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
5
let no_prefix = remove_prefix(cert_data);
6
let decoded = decode_base45(no_prefix);
7
let decompressed = decompress(decoded);
8
let cwt_payload = get_cwt_payload(decompressed);
9
let parsed_payload = parse_cwt_payload(cwt_payload);
10
11
}
12
13
// ...
14
use ciborium::{de::from_reader, value::Value};
use serde_json::to_string_pretty;
fn main() {
let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4
let no_prefix = remove_prefix(cert_data);
let decoded = decode_base45(no_prefix);
let decompressed = decompress(decoded);
let cwt_payload = get_cwt_payload(decompressed);
let parsed_payload = parse_cwt_payload(cwt_payload);
println!("{}", to_string_pretty(&parsed_payload).unwrap());
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
loige 64
loige 65
All the code:
loige.link/green-code
loige 66
A better (& more complete) implementation
as a Rust library
loige
github.com/rust-italia/dgc
67
Exercise for the viewer:
Try to validate the signature
loige
🔑You can get the Public Key from the certificate
here: loige.link/green-examples
📑Here you can find more about how the
CoseSign1 protocol works: loige.link/cose-sign-
verif
📦You could use a crate like for crypto!
ring
(Spoiler: We implemented some of this stuff in the library!)
dgc
68
Is all this stuff legal? 😰
loige
👀You can certainly look into your certificate (and
the test certificates!)
🗣Looking into other people's certificate will
disclose a lot of privacy-sensitive info (thread
carefully)
📲Building a validator app? Check your country's
regulation (especially if you need to store data!)
69
Cover Picture by on
❤  Huge thanks to for some precius review sessions and many pull requests!
❤ Thanks to , , , ,  for reviews and suggestions.
FPVmat A Unsplash
rust-italia
@gbinside @88_eugen @AlleviTommaso @npmccallum @pelger
loige
☝nodejsdp.link
loige.link/rust-green
THANK YOU!
❤
70

Más contenido relacionado

Similar a A look inside the European Covid Green Certificate - Rust Dublin

Exploring Thermal Related Stuff in iDevices using Open-Source Tool
Exploring Thermal Related Stuff in iDevices using Open-Source ToolExploring Thermal Related Stuff in iDevices using Open-Source Tool
Exploring Thermal Related Stuff in iDevices using Open-Source ToolKoan-Sin Tan
 
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015Christian Hallqvist
 
Sourcefire Vulnerability Research Team Labs
Sourcefire Vulnerability Research Team LabsSourcefire Vulnerability Research Team Labs
Sourcefire Vulnerability Research Team Labslosalamos
 
Building and Scaling the Internet of Things with MongoDB at Vivint
Building and Scaling the Internet of Things with MongoDB at Vivint Building and Scaling the Internet of Things with MongoDB at Vivint
Building and Scaling the Internet of Things with MongoDB at Vivint MongoDB
 
I made some more expansion board for M5Stack
I made some more expansion  board for M5StackI made some more expansion  board for M5Stack
I made some more expansion board for M5StackMasawo Yamazaki
 
Filippo, Plain simple reality of entropy
Filippo, Plain simple reality of  entropyFilippo, Plain simple reality of  entropy
Filippo, Plain simple reality of entropyPacSecJP
 
How to Speak Intel DPDK KNI for Web Services.
How to Speak Intel DPDK KNI for Web Services.How to Speak Intel DPDK KNI for Web Services.
How to Speak Intel DPDK KNI for Web Services.Naoto MATSUMOTO
 
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...Piccoli Green Technology Piccoli
 
GAS LEAKAGE DETECTOR
GAS LEAKAGE DETECTORGAS LEAKAGE DETECTOR
GAS LEAKAGE DETECTORIRJET Journal
 
Проблемы использования TCP в мобильных приложениях. Владимир Кириллов
Проблемы использования TCP в мобильных приложениях.  Владимир КирилловПроблемы использования TCP в мобильных приложениях.  Владимир Кириллов
Проблемы использования TCP в мобильных приложениях. Владимир КирилловAnthony Marchenko
 
Cannot observe ingress packets
Cannot observe ingress packetsCannot observe ingress packets
Cannot observe ingress packetssoichi shigeta
 
NoSQL oder: Freiheit ist nicht schmerzfrei - IT Tage
NoSQL oder: Freiheit ist nicht schmerzfrei - IT TageNoSQL oder: Freiheit ist nicht schmerzfrei - IT Tage
NoSQL oder: Freiheit ist nicht schmerzfrei - IT TageAlexander Hendorf
 
Доклад Антона Поварова "Go in Badoo" с Golang Meetup
Доклад Антона Поварова "Go in Badoo" с Golang MeetupДоклад Антона Поварова "Go in Badoo" с Golang Meetup
Доклад Антона Поварова "Go in Badoo" с Golang MeetupBadoo Development
 
OSSNA 2017 Performance Analysis Superpowers with Linux BPF
OSSNA 2017 Performance Analysis Superpowers with Linux BPFOSSNA 2017 Performance Analysis Superpowers with Linux BPF
OSSNA 2017 Performance Analysis Superpowers with Linux BPFBrendan Gregg
 

Similar a A look inside the European Covid Green Certificate - Rust Dublin (20)

Exploring Thermal Related Stuff in iDevices using Open-Source Tool
Exploring Thermal Related Stuff in iDevices using Open-Source ToolExploring Thermal Related Stuff in iDevices using Open-Source Tool
Exploring Thermal Related Stuff in iDevices using Open-Source Tool
 
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015
IDS-ETHZ - NSG-IDS-REPORTING_of_year_2015
 
Sourcefire Vulnerability Research Team Labs
Sourcefire Vulnerability Research Team LabsSourcefire Vulnerability Research Team Labs
Sourcefire Vulnerability Research Team Labs
 
Jacdelhi seat matrix
Jacdelhi seat matrixJacdelhi seat matrix
Jacdelhi seat matrix
 
From logs to metrics
From logs to metricsFrom logs to metrics
From logs to metrics
 
Building and Scaling the Internet of Things with MongoDB at Vivint
Building and Scaling the Internet of Things with MongoDB at Vivint Building and Scaling the Internet of Things with MongoDB at Vivint
Building and Scaling the Internet of Things with MongoDB at Vivint
 
Advanced Computational Drug Design
Advanced Computational Drug DesignAdvanced Computational Drug Design
Advanced Computational Drug Design
 
I made some more expansion board for M5Stack
I made some more expansion  board for M5StackI made some more expansion  board for M5Stack
I made some more expansion board for M5Stack
 
Network Security
Network SecurityNetwork Security
Network Security
 
Filippo, Plain simple reality of entropy
Filippo, Plain simple reality of  entropyFilippo, Plain simple reality of  entropy
Filippo, Plain simple reality of entropy
 
Franquia green sharing IOT Scooters devices
Franquia green sharing  IOT Scooters devicesFranquia green sharing  IOT Scooters devices
Franquia green sharing IOT Scooters devices
 
How to Speak Intel DPDK KNI for Web Services.
How to Speak Intel DPDK KNI for Web Services.How to Speak Intel DPDK KNI for Web Services.
How to Speak Intel DPDK KNI for Web Services.
 
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...
PICCOLI GREEN TECHNOLOGY , PICCOLI MOTORS , PGT GROUP, Franquia Piccoli Green...
 
GAS LEAKAGE DETECTOR
GAS LEAKAGE DETECTORGAS LEAKAGE DETECTOR
GAS LEAKAGE DETECTOR
 
Проблемы использования TCP в мобильных приложениях. Владимир Кириллов
Проблемы использования TCP в мобильных приложениях.  Владимир КирилловПроблемы использования TCP в мобильных приложениях.  Владимир Кириллов
Проблемы использования TCP в мобильных приложениях. Владимир Кириллов
 
Cannot observe ingress packets
Cannot observe ingress packetsCannot observe ingress packets
Cannot observe ingress packets
 
NoSQL oder: Freiheit ist nicht schmerzfrei - IT Tage
NoSQL oder: Freiheit ist nicht schmerzfrei - IT TageNoSQL oder: Freiheit ist nicht schmerzfrei - IT Tage
NoSQL oder: Freiheit ist nicht schmerzfrei - IT Tage
 
Доклад Антона Поварова "Go in Badoo" с Golang Meetup
Доклад Антона Поварова "Go in Badoo" с Golang MeetupДоклад Антона Поварова "Go in Badoo" с Golang Meetup
Доклад Антона Поварова "Go in Badoo" с Golang Meetup
 
Quic illustrated
Quic illustratedQuic illustrated
Quic illustrated
 
OSSNA 2017 Performance Analysis Superpowers with Linux BPF
OSSNA 2017 Performance Analysis Superpowers with Linux BPFOSSNA 2017 Performance Analysis Superpowers with Linux BPF
OSSNA 2017 Performance Analysis Superpowers with Linux BPF
 

Más de Luciano Mammino

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSLuciano Mammino
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoLuciano Mammino
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperLuciano Mammino
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Luciano Mammino
 
Everything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsEverything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsLuciano Mammino
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableLuciano Mammino
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Luciano Mammino
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaLuciano Mammino
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)Luciano Mammino
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made SimpleLuciano Mammino
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessLuciano Mammino
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Luciano Mammino
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Luciano Mammino
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3Luciano Mammino
 

Más de Luciano Mammino (20)

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJS
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiper
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!
 
Everything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsEverything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLs
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & Airtable
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀
 
Monoliths to the cloud!
Monoliths to the cloud!Monoliths to the cloud!
Monoliths to the cloud!
 
The senior dev
The senior devThe senior dev
The senior dev
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made Simple
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti Serverless
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 

Último

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 

Último (20)

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 

A look inside the European Covid Green Certificate - Rust Dublin

  • 1. A look inside the European Covid Green Certificate Luciano Mammino ( ) @loige loige.link/rust-green 22-02-2022 😱 1
  • 3. 👋I'm Luciano Senior Architect @ fourTheorem (Dublin ) nodejsdp.link 📔Co-Author of Node.js Design Patterns  👉 Let's connect!   (blog)   (twitter)   (twitch)   (github) loige.co @loige loige lmammino 3
  • 4. We are business focused technologists that deliver.  |  | Accelerated Serverless AI as a Service Platform Modernisation We are hiring: do you want to ? work with us loige 4
  • 5. loige 🦀I'm learning Rust as a hobby... Live streaming my attempts to crack Advent of Code in Rust (with and ): / @gbinside @88_eugen Twitch YouTube Wrote articles: a few How to to_string in Rust Rust shenanigans: return type polymorphism Where to go to learn Rust in 2021 Published (simple) crates: , , , a few jwtinfo allwords gmaps-static dgc 5
  • 6. Disclaimers loige 🤓I am not involved with the DGC working group   😢COVID has been tough on everyone,       we'll try to focus only on the tech here! 6
  • 7. Agenda + Goals loige 1. Needs and principles 2. 🗝Cryptographic model 3. 📦The data 4. 🧅Layers of encoding 5. 🛠Decoding in Rust 🤨Learn some cool technologies 🧐Learn a tiny bit of Rust 🤓Be nerdy and have fun! 7
  • 8. The need for a digital certificate in the COVID age loige 8
  • 9. The need for a digital certificate in the COVID age loige 😷We need a system to quickly provide a proof against COVID       (Vaccination, negative test, proof of recovery)  It needs to be personal, easy to carry around (digital),        easy to issue and to validate 🌎It needs to be secure against forgery and work across countries 9
  • 10. The EU Covid Green Pass a.k.a.  Electronic Health Certificates (HCERT)   loige.link/hcert-spec loige 10
  • 11. Electronic Health Certificates (HCERT) Requirements & Guiding Principles loige ✍Signed data with machine readable content 📃Use compact encoding 🤲Based on open standards 11
  • 12. Asymmetric cryptographic signatures loige 🤫 Private Key 📢 Public Key 🔗 101010101000101010010... 0101010101010101010101... 12
  • 13. Asymmetric cryptographic signatures loige 🤫 Private Key 📢 Public Key 101010101000101010010... 0101010101010101010101... The owner of the private key signs the document Anyone can validate the signature using the public key 13
  • 14. What's inside a certificate? loige DGC container Cryptographic header (Key Id, Algorithm) Cryptographic Signature Header (Issuer, Issue date, expiry date) 14 Certificates list vaccine, test, or recovery data Personal data (name, surname, DoB)
  • 16. { "1": "DK", "4": 1625054000, "6": 1622462000, "-260": { "ver":"1.0.0", "nam":{ "fn":"Klaus", "fnt":"KLAUS", "gn":"Jørgensen", "gnt":"JOERGENSEN" }, "dob":"1983-01-06", "v":[ { "tg":"840539006", "vp":"1119349007", "mp":"EU/1/20/1528", "ma":"ORG-100030215", "dn":2, "sd":2, "dt":"2021-05-03", "co":"DK", "is":"Danish Health Data Authority", "ci":"URN:UVCI:01:DK:B986830007345F99AE898FB82C6C61F2#A" } ] } } An example loige loige.link/green-examples Personal info Vaccine info DGC Header 16
  • 17. Layered encoding loige QRCODE ASCII mode Prefix + Base45 encoding Zlib compression CWT signed data (COSE) CBOR document 17 🧅
  • 20. loige Allows to encode binary data in text format (ASCII) Like Base64, but it uses 45 characters instead: loige.link/base45-explained Base45 20
  • 21. Base45 loige 01001001 00100000 01100111 01101111 01110100 00100000 01101101 01111001 00100000 01110011 01101000 01101111 01110100 01110011 00100000 11011000 00111101 UTF8 (17 bytes) I got my shots 💉 Hex (38 bytes) 49 20 67 6f 74 20 6d 79 20 73 68 6f 74 73 20 f0 9f 92 89 Base64 (28 bytes) SSBnb3QgbXkgc2hvdHMg8J+SiQ== Base45 (29 bytes) 0B9J3DSUEZ$DR4459DLWEH74Z7K23 Some binary data 21
  • 22. loige loige.link/base45-rfc "A QR-code is used to encode text as a graphical image. [...] QR- codes cannot be used to encode arbitrary binary data directly.  [...] Compared to already established Base64, Base32 and Base16 encoding schemes [...], the Base45 scheme described in this document offer a more compact QR-code encoding" Base45 22
  • 25. Zlib compression loige "zlib is designed to be a free, general-purpose, legally unencumbered -- that is, not covered by any patents -- lossless data-compression library for use on virtually any computer hardware and operating system" zlib.net 25
  • 26. Zlib compression loige Zlib inflate Compressed binary data Decompressed binary data 26
  • 27. Zlib compression loige 👀We start to see some "readable" information! 27
  • 28. COSE / CWT loige CBOR Object Signing and Encryption / CBOR Web Token 28
  • 29. CBOR !? 🤔 loige But wait, what the heck is ✋ 29
  • 30. CBOR loige TLDR; Like JSON but in binary! loige.link/cbor-rfc 30
  • 31. JSON loige A schema-less data format where a value can be: Null Boolean Number String Array Object null true -17.34 "A programmer walks into a bar..." ["foo", 1.23, null, false, [22]] {"foo": "bar", "manyvals": [1,2,3], "nested": {}} 31
  • 32. CBOR loige A schema-less binary data format where a value can be: Null Boolean Number String Text Array Object Map F6 F5 fbc031570a3d70a3d7 7820412070726f6772616d6d65722077616c6b7320696e746f2061206261722e2e2e 8563666f6ffb3ff3ae147ae147aef6f48116 a363666f6f63626172686d616e7976616c7383010203666e6573746564a0 32
  • 33. CBOR loige It also supports: Byte String (a sequence of raw bytes) Tags (annotations that allow to create new types) 33
  • 34. CBOR loige An example: A3 63666F6F 63626172 686D616E7976616C73 83 01 02 03 666E6573746564 A0 { "foo": "bar", "manyvals": [ } ], "nested": {} 1, 2, 3 34
  • 35. COSE / CWT loige CBOR Object Signing and Encryption / CBOR Web Token ok... again 😅 35
  • 36. COSE loige loige.link/cose-rfc CBOR Object Signing and Encryption COSE (inspired by ) defines CBOR-based protocols for: JOSE Encrypted data Cryptographic signed data MACed data 36
  • 37. CWT loige Like but for CBOR JWT loige.link/cwt-rfc Defines a protocol for transferring claims between parties CBOR Web Token Claims are digitally signed for authenticity 37
  • 38. CWT loige A CWT is made of 4 parts: Protected header CBOR Web Token Non protected header Payload Signature 38
  • 39. CWT loige A CWT is encoded as a (tagged) CBOR array with 4 values: Protected header (binary string) CBOR Web Token Non protected header (map) Payload (binary string) Signature (binary string) 39
  • 40. Protected header Binary String loige Array (length 4) CWT tag 40 Unprotected header Map (length 0) Payload Binary String Signature Binary String
  • 41. loige CWT payload { "1": "DK", "4": 1625054000, "6": 1622462000, "-260": { "ver":"1.0.0", "nam":{ "fn":"Klaus", "fnt":"KLAUS", "gn":"Jørgensen", "gnt":"JOERGENSEN" }, "dob":"1983-01-06", "v":[ { "tg":"840539006", "vp":"1119349007", "mp":"EU/1/20/1528", "ma":"ORG-100030215", "dn":2, "sd":2, "dt":"2021-05-03", "co":"DK", "is":"Danish Health Data Authority", "ci":"URN:UVCI:01:DK:B986830007345F99AE898FB82C6C61F2#A" } ] } } Binary String follows (CBOR Encoded) CBOR decode     (to JSON) 41
  • 42. How to decode - quick recap loige 1. Remove "HC1:" prefix 2. Base45 decode 3. Zlib decompress 4. Parse CWT 5. Parse CWT Payload as CBOR 6. Party hard! 🥳 Hey, let's implement this... in Rust! 42
  • 44. // src/main.rs fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 todo!() // 1. Remove "HC1:" prefix // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } 1 2 3 4 5 6 7 8 9 10 11 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 // src/main.rs 1 2 fn main() { 3 4 todo!() 5 // 1. Remove "HC1:" prefix 6 // 2. Base45 decode 7 // 3. Zlib decompress 8 // 4. Parse CWT 9 // 5. Parse CWT Payload as CBOR 10 } 11 todo!() // 1. Remove "HC1:" prefix // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR // src/main.rs 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 5 6 7 8 9 10 } 11 // src/main.rs fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 todo!() // 1. Remove "HC1:" prefix // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } 1 2 3 4 5 6 7 8 9 10 11 loige 44
  • 45. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); todo!(); // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn remove_prefix(data: &str) -> &str { todo!() // remove "HC1:" prefix and return remaining string } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let no_prefix = remove_prefix(cert_data); fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 3 todo!(); 4 // 2. Base45 decode 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn remove_prefix(data: &str) -> &str { 11 todo!() 12 // remove "HC1:" prefix and return remaining string 13 } 14 fn remove_prefix(data: &str) -> &str { todo!() // remove "HC1:" prefix and return remaining string } fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 todo!(); 4 // 2. Base45 decode 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 11 12 13 14 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); todo!(); // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn remove_prefix(data: &str) -> &str { todo!() // remove "HC1:" prefix and return remaining string } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 loige 45
  • 46. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); todo!(); // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn remove_prefix(data: &str) -> &str { if data.len() < 4 || !data.starts_with("HC1:") { panic!("Invalid prefix"); // IRL use a Result! } &data[4..] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if data.len() < 4 || !data.starts_with("HC1:") { panic!("Invalid prefix"); // IRL use a Result! } &data[4..] fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 todo!(); 4 // 2. Base45 decode 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn remove_prefix(data: &str) -> &str { 11 12 13 14 15 16 } 17 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); todo!(); // 2. Base45 decode // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn remove_prefix(data: &str) -> &str { if data.len() < 4 || !data.starts_with("HC1:") { panic!("Invalid prefix"); // IRL use a Result! } &data[4..] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 loige 46
  • 47. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); todo!() // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decode_base45(data: &str) -> Vec<u8> { todo!() // parse the string as base45 encoded and return the // resulting raw bytes } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let decoded = decode_base45(no_prefix); fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 4 todo!() 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn decode_base45(data: &str) -> Vec<u8> { 11 todo!() 12 // parse the string as base45 encoded and return the 13 // resulting raw bytes 14 } 15 16 // ... 17 fn decode_base45(data: &str) -> Vec<u8> { todo!() // parse the string as base45 encoded and return the // resulting raw bytes } fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 todo!() 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 11 12 13 14 15 16 // ... 17 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); todo!() // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decode_base45(data: &str) -> Vec<u8> { todo!() // parse the string as base45 encoded and return the // resulting raw bytes } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 loige 47
  • 48. loige cargo add base45 # Cargo.toml # ... [dependencies] base45 = "3.0.0" 48
  • 49. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); todo!() // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decode_base45(data: &str) -> Vec<u8> { base45::decode(data).unwrap() // IRL use a Result! } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 base45::decode(data).unwrap() // IRL use a Result! fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 todo!() 5 // 3. Zlib decompress 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn decode_base45(data: &str) -> Vec<u8> { 11 12 } 13 14 // ... 15 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); todo!() // 3. Zlib decompress // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decode_base45(data: &str) -> Vec<u8> { base45::decode(data).unwrap() // IRL use a Result! } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 loige 49
  • 50. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); todo!() // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decompress(data: Vec<u8>) -> Vec<u8> { todo!() // decompress using zlib inflate } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let decompressed = decompress(decoded); fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 5 todo!() 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn decompress(data: Vec<u8>) -> Vec<u8> { 11 todo!() 12 // decompress using zlib inflate 13 } 14 15 // ... 16 fn decompress(data: Vec<u8>) -> Vec<u8> { todo!() // decompress using zlib inflate } fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 let decompressed = decompress(decoded); 5 todo!() 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 11 12 13 14 15 // ... 16 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); todo!() // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decompress(data: Vec<u8>) -> Vec<u8> { todo!() // decompress using zlib inflate } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 loige 50
  • 51. loige cargo add inflate # Cargo.toml # ... [dependencies] base45 = "3.0.0" inflate = "0.4.5" 51
  • 52. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); todo!() // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decompress(data: Vec<u8>) -> Vec<u8> { inflate::inflate_bytes_zlib(data.as_slice()).unwrap() // IRL use a Result! } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 inflate::inflate_bytes_zlib(data.as_slice()).unwrap() // IRL use a Result! fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 let decompressed = decompress(decoded); 5 todo!() 6 // 4. Parse CWT 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn decompress(data: Vec<u8>) -> Vec<u8> { 11 12 13 } 14 15 // ... 16 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); todo!() // 4. Parse CWT // 5. Parse CWT Payload as CBOR } fn decompress(data: Vec<u8>) -> Vec<u8> { inflate::inflate_bytes_zlib(data.as_slice()).unwrap() // IRL use a Result! } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 loige 52
  • 53. Are we on the right track? loige fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); println!("{}", String::from_utf8_lossy(&decompressed)); } // ... 1 2 3 4 5 6 7 8 9 10 println!("{}", String::from_utf8_lossy(&decompressed)); fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 let decompressed = decompress(decoded); 5 6 7 } 8 9 // ... 10 53
  • 54. Are we on the right track? loige We are starting to see some readable info! 🤩 54
  • 55. fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); todo!() // 5. Parse CWT Payload as CBOR } fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { todo!() // Parse raw bytes as CBOR. // Extract and return the raw bytes representing // the CWT payload } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let cwt_payload = get_cwt_payload(decompressed); fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 let decompressed = decompress(decoded); 5 6 todo!() 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 11 todo!() 12 // Parse raw bytes as CBOR. 13 // Extract and return the raw bytes representing 14 // the CWT payload 15 } 16 17 // ... 18 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { todo!() // Parse raw bytes as CBOR. // Extract and return the raw bytes representing // the CWT payload } fn main() { 1 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 2 let no_prefix = remove_prefix(cert_data); 3 let decoded = decode_base45(no_prefix); 4 let decompressed = decompress(decoded); 5 let cwt_payload = get_cwt_payload(decompressed); 6 todo!() 7 // 5. Parse CWT Payload as CBOR 8 } 9 10 11 12 13 14 15 16 17 // ... 18 fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); todo!() // 5. Parse CWT Payload as CBOR } fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { todo!() // Parse raw bytes as CBOR. // Extract and return the raw bytes representing // the CWT payload } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 loige 55
  • 56. loige cargo add ciborium # Cargo.toml # ... [dependencies] base45 = "3.0.0" ciborium = "0.2.0" inflate = "0.4.5" 56
  • 57. use ciborium::{de::from_reader, value::Value}; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); todo!() // 5. Parse CWT Payload as CBOR } fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { let parsed: Value = from_reader(data.as_slice()).unwrap(); println!("{:?}", parsed); todo!() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 todo!() 9 // 5. Parse CWT Payload as CBOR 10 } 11 12 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 13 let parsed: Value = from_reader(data.as_slice()).unwrap(); 14 println!("{:?}", parsed); 15 todo!() 16 } 17 18 // ... 19 let parsed: Value = from_reader(data.as_slice()).unwrap(); println!("{:?}", parsed); use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 todo!() 9 // 5. Parse CWT Payload as CBOR 10 } 11 12 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 13 14 15 todo!() 16 } 17 18 // ... 19 loige 57
  • 59. // ... fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { // IRL avoid .unwrap() like hell and propagate errors correctly! let parsed: Value = from_reader(data.as_slice()).unwrap(); let (tag, arr) = parsed.as_tag().unwrap(); assert_eq!(tag, 18); let arr = arr.as_array().unwrap(); let payload = arr[2].as_bytes().unwrap(); payload.clone() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 let parsed: Value = from_reader(data.as_slice()).unwrap(); // ... 1 2 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 3 // IRL avoid .unwrap() like hell and propagate errors correctly! 4 5 let (tag, arr) = parsed.as_tag().unwrap(); 6 assert_eq!(tag, 18); 7 let arr = arr.as_array().unwrap(); 8 let payload = arr[2].as_bytes().unwrap(); 9 payload.clone() 10 } 11 12 // ... 13 let (tag, arr) = parsed.as_tag().unwrap(); assert_eq!(tag, 18); // ... 1 2 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 3 // IRL avoid .unwrap() like hell and propagate errors correctly! 4 let parsed: Value = from_reader(data.as_slice()).unwrap(); 5 6 7 let arr = arr.as_array().unwrap(); 8 let payload = arr[2].as_bytes().unwrap(); 9 payload.clone() 10 } 11 12 // ... 13 let arr = arr.as_array().unwrap(); // ... 1 2 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 3 // IRL avoid .unwrap() like hell and propagate errors correctly! 4 let parsed: Value = from_reader(data.as_slice()).unwrap(); 5 let (tag, arr) = parsed.as_tag().unwrap(); 6 assert_eq!(tag, 18); 7 8 let payload = arr[2].as_bytes().unwrap(); 9 payload.clone() 10 } 11 12 // ... 13 let payload = arr[2].as_bytes().unwrap(); payload.clone() // ... 1 2 fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { 3 // IRL avoid .unwrap() like hell and propagate errors correctly! 4 let parsed: Value = from_reader(data.as_slice()).unwrap(); 5 let (tag, arr) = parsed.as_tag().unwrap(); 6 assert_eq!(tag, 18); 7 let arr = arr.as_array().unwrap(); 8 9 10 } 11 12 // ... 13 // ... fn get_cwt_payload(data: Vec<u8>) -> Vec<u8> { // IRL avoid .unwrap() like hell and propagate errors correctly! let parsed: Value = from_reader(data.as_slice()).unwrap(); let (tag, arr) = parsed.as_tag().unwrap(); assert_eq!(tag, 18); let arr = arr.as_array().unwrap(); let payload = arr[2].as_bytes().unwrap(); payload.clone() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 loige 59
  • 60. use ciborium::{de::from_reader, value::Value}; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); let parsed_payload = parse_cwt_payload(cwt_payload); } fn parse_cwt_payload(data: Vec<u8>) -> Value { // parse the binary data as CBOR todo!() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let parsed_payload = parse_cwt_payload(cwt_payload); use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 9 } 10 11 fn parse_cwt_payload(data: Vec<u8>) -> Value { 12 // parse the binary data as CBOR 13 todo!() 14 } 15 16 // ... 17 fn parse_cwt_payload(data: Vec<u8>) -> Value { // parse the binary data as CBOR todo!() } use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 let parsed_payload = parse_cwt_payload(cwt_payload); 9 } 10 11 12 13 14 15 16 // ... 17 loige 60
  • 61. use ciborium::{de::from_reader, value::Value}; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); let parsed_payload = parse_cwt_payload(cwt_payload); println!("{:#?}", parsed_payload); } fn parse_cwt_payload(data: Vec<u8>) -> Value { from_reader(data.as_slice()).unwrap() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from_reader(data.as_slice()).unwrap() use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 let parsed_payload = parse_cwt_payload(cwt_payload); 9 println!("{:#?}", parsed_payload); 10 } 11 12 fn parse_cwt_payload(data: Vec<u8>) -> Value { 13 14 } 15 16 // ... 17 println!("{:#?}", parsed_payload); use ciborium::{de::from_reader, value::Value}; 1 2 fn main() { 3 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 4 let no_prefix = remove_prefix(cert_data); 5 let decoded = decode_base45(no_prefix); 6 let decompressed = decompress(decoded); 7 let cwt_payload = get_cwt_payload(decompressed); 8 let parsed_payload = parse_cwt_payload(cwt_payload); 9 10 } 11 12 fn parse_cwt_payload(data: Vec<u8>) -> Value { 13 from_reader(data.as_slice()).unwrap() 14 } 15 16 // ... 17 use ciborium::{de::from_reader, value::Value}; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); let parsed_payload = parse_cwt_payload(cwt_payload); println!("{:#?}", parsed_payload); } fn parse_cwt_payload(data: Vec<u8>) -> Value { from_reader(data.as_slice()).unwrap() } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 loige 61
  • 63. Ok, let's make it more readable... 😅 loige cargo add serde_json 63
  • 64. use ciborium::{de::from_reader, value::Value}; use serde_json::to_string_pretty; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); let parsed_payload = parse_cwt_payload(cwt_payload); println!("{}", to_string_pretty(&parsed_payload).unwrap()); } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 use serde_json::to_string_pretty; use ciborium::{de::from_reader, value::Value}; 1 2 3 fn main() { 4 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 5 let no_prefix = remove_prefix(cert_data); 6 let decoded = decode_base45(no_prefix); 7 let decompressed = decompress(decoded); 8 let cwt_payload = get_cwt_payload(decompressed); 9 let parsed_payload = parse_cwt_payload(cwt_payload); 10 println!("{}", to_string_pretty(&parsed_payload).unwrap()); 11 } 12 13 // ... 14 println!("{}", to_string_pretty(&parsed_payload).unwrap()); use ciborium::{de::from_reader, value::Value}; 1 use serde_json::to_string_pretty; 2 3 fn main() { 4 let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 5 let no_prefix = remove_prefix(cert_data); 6 let decoded = decode_base45(no_prefix); 7 let decompressed = decompress(decoded); 8 let cwt_payload = get_cwt_payload(decompressed); 9 let parsed_payload = parse_cwt_payload(cwt_payload); 10 11 } 12 13 // ... 14 use ciborium::{de::from_reader, value::Value}; use serde_json::to_string_pretty; fn main() { let cert_data = "HC1:NCFOXN%TSMAHN-H9QCGDSB5QPN9OO3:D4$X4-365KN-TMLV4 let no_prefix = remove_prefix(cert_data); let decoded = decode_base45(no_prefix); let decompressed = decompress(decoded); let cwt_payload = get_cwt_payload(decompressed); let parsed_payload = parse_cwt_payload(cwt_payload); println!("{}", to_string_pretty(&parsed_payload).unwrap()); } // ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 loige 64
  • 67. A better (& more complete) implementation as a Rust library loige github.com/rust-italia/dgc 67
  • 68. Exercise for the viewer: Try to validate the signature loige 🔑You can get the Public Key from the certificate here: loige.link/green-examples 📑Here you can find more about how the CoseSign1 protocol works: loige.link/cose-sign- verif 📦You could use a crate like for crypto! ring (Spoiler: We implemented some of this stuff in the library!) dgc 68
  • 69. Is all this stuff legal? 😰 loige 👀You can certainly look into your certificate (and the test certificates!) 🗣Looking into other people's certificate will disclose a lot of privacy-sensitive info (thread carefully) 📲Building a validator app? Check your country's regulation (especially if you need to store data!) 69
  • 70. Cover Picture by on ❤  Huge thanks to for some precius review sessions and many pull requests! ❤ Thanks to , , , ,  for reviews and suggestions. FPVmat A Unsplash rust-italia @gbinside @88_eugen @AlleviTommaso @npmccallum @pelger loige ☝nodejsdp.link loige.link/rust-green THANK YOU! ❤ 70