Weitere ähnliche Inhalte Ähnlich wie A look inside the European Covid Green Certificate - Rust Dublin (20) Mehr von Luciano Mammino (20) Kürzlich hochgeladen (20) A look inside the European Covid Green Certificate - Rust Dublin1. 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
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
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
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
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
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
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
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
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