When I saw how dense the European Covid Green Pass QR code is, I got immediately curious: "WOW, there must be a lot of interesting data in here". So, I started to dig deeper and I found that there's really a great wealth of interesting encoding and verification technologies being used in it! In this talk, I will share what I learned! We will go on a journey where we will explore Base54 encoding, COSE tokens, CBOR serialization, elliptic curve crypto, and much more! Finally, I will also show you how to write a decoder for Green Pass certificates in the most hyped language ever: Rust!
8. 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
11. 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!
5
13. Agenda + Goals
loige
1. Needs and principles
2. šCryptographic model
3. š¦The data
4. š§ Layers of encoding
5. š Decoding in Rust
6
14. 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!
6
15. The need for a digital certiļ¬cate in
the COVID age
loige 7
16. The need for a digital certiļ¬cate in
the COVID age
loige 8
17. The need for a digital certiļ¬cate in
the COVID age
loige
š·We need a system to quickly provide a proof against COVID
Ā Ā Ā (Vaccination, negative test, proof of recovery)Ā
8
18. The need for a digital certiļ¬cate 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
8
19. The need for a digital certiļ¬cate 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
8
20. The EU Covid Green Pass
a.k.a.Ā
Electronic Health Certiļ¬cates
(HCERT)
Ā
loige.link/hcert-spec
loige 9
22. Electronic Health Certiļ¬cates (HCERT)
Requirements & Guiding Principles
loige
āSigned data with machine readable content
10
23. Electronic Health Certiļ¬cates (HCERT)
Requirements & Guiding Principles
loige
āSigned data with machine readable content
šUse compact encoding
10
24. Electronic Health Certiļ¬cates (HCERT)
Requirements & Guiding Principles
loige
āSigned data with machine readable content
šUse compact encoding
š¤²Based on open standards
10
33. 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
12
34. 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
12
71. 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
21
77. 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
24
88. 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": {}}
30
89. 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
31
105. COSE
loige
CBOR Object Signing and Encryption
COSE (inspired by ) deļ¬nes CBOR-based protocols for:
JOSE
Encrypted data
35
106. COSE
loige
CBOR Object Signing and Encryption
COSE (inspired by ) deļ¬nes CBOR-based protocols for:
JOSE
Encrypted data
Cryptographic signed data
35
107. COSE
loige
CBOR Object Signing and Encryption
COSE (inspired by ) deļ¬nes CBOR-based protocols for:
JOSE
Encrypted data
Cryptographic signed data
MACed data
35
111. CWT
loige
Like but for CBOR
JWT
Deļ¬nes a protocol for transferring claims between parties
CBOR Web Token
36
112. CWT
loige
Like but for CBOR
JWT
Deļ¬nes a protocol for transferring claims between parties
CBOR Web Token
Claims are digitally signed for authenticity
36
113. CWT
loige
Like but for CBOR
JWT
loige.link/cwt-rfc
Deļ¬nes a protocol for transferring claims between parties
CBOR Web Token
Claims are digitally signed for authenticity
36
117. CWT
loige
A CWT is made of 4 parts:
Protected header
CBOR Web Token
Non protected header
37
118. CWT
loige
A CWT is made of 4 parts:
Protected header
CBOR Web Token
Non protected header
Payload
37
119. CWT
loige
A CWT is made of 4 parts:
Protected header
CBOR Web Token
Non protected header
Payload
Signature
37
120. 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)
38
176. 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 51
177. 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
loige 51
178. 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 51
179. 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
52
180. 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
52
224. A better (& more complete) implementation
as a Rust library
loige
github.com/rust-italia/dgc
66
225. Exercise for the viewer:
Try to validate the signature
loige 67
226. Exercise for the viewer:
Try to validate the signature
loige
šYou can get the Public Key from the certiļ¬cate
here: loige.link/green-examples
67
227. Exercise for the viewer:
Try to validate the signature
loige
šYou can get the Public Key from the certiļ¬cate
here: loige.link/green-examples
šHere you can ļ¬nd more about how the CoseSign1
protocol works: loige.link/cose-sign-verif
67
228. Exercise for the viewer:
Try to validate the signature
loige
šYou can get the Public Key from the certiļ¬cate
here: loige.link/green-examples
šHere you can ļ¬nd more about how the CoseSign1
protocol works: loige.link/cose-sign-verif
š¦You could use a crate like for crypto!
ring
67
229. Exercise for the viewer:
Try to validate the signature
loige
šYou can get the Public Key from the certiļ¬cate
here: loige.link/green-examples
šHere you can ļ¬nd more about how the CoseSign1
protocol works: loige.link/cose-sign-verif
š¦You could use a crate like for crypto!
ring
(Spoiler: I implemented some of this stuff in my library!)
67
231. Is all this stuļ¬ legal? š°
loige
šYou can certainly look into your certiļ¬cate (and the
test certiļ¬cates!)
68
232. Is all this stuļ¬ legal? š°
loige
šYou can certainly look into your certiļ¬cate (and the
test certiļ¬cates!)
š£Looking into other people's certiļ¬cate will disclose
a lot of privacy-sensitive info (thread carefully)
68
233. Is all this stuļ¬ legal? š°
loige
šYou can certainly look into your certiļ¬cate (and the
test certiļ¬cates!)
š£Looking into other people's certiļ¬cate 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!)
68
234. 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/green
THANK YOU!
ā¤
69