-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdcc-validator.go
150 lines (124 loc) · 3.77 KB
/
dcc-validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package dcc
import (
"crypto"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"github.com/veraison/go-cose"
)
var (
API_BASE_URL = "https://get.dgc.gov.it/v1/dgc"
)
// Verify a Vaccine Passport's signature, it reads the *cose.Sign1Message parameter and returns the status and/or error.
// This function makes network requests (HTTP) to fetch the KIDs to verify the pass
func Verify(raw *cose.Sign1Message) (valid bool, err error) {
// Fetch KIDs to compare
kidsList, err := fetchValidKIDs()
if err != nil {
return
}
// Write PEMs into KIDs map
kids, err := fetchCerts(kidsList)
if err != nil {
return
}
// Extract KID from Passports Header
kidBytes, err := extractHeaderBytes(raw, cose.HeaderLabelKeyID)
if err != nil {
return
}
var kid = base64.StdEncoding.EncodeToString(kidBytes)
// Check KID is in trusted list
if _, ok := kids[kid]; !ok {
return false, errors.New("KID in Pass not recognized, cannot verify")
}
// Parse Signer certificate belonging to KID. Extract public key
pemCertificate := kids[kid]
block, _ := pem.Decode([]byte(pemCertificate))
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return
}
publicKey := cert.PublicKey.(crypto.PublicKey)
verifier, _ := cose.NewVerifier(cose.AlgorithmES256, publicKey)
err = raw.Verify(nil, verifier)
if err != nil {
return false, err
}
return true, err
}
// fetchCerts fetches all the Signer Certificates to be used to validate International Vaccine Passports
func fetchCerts(kids []string) (map[string]string, error) {
var kidsMap = map[string]string{}
var token = "0"
for range kids {
// Generate request wit headers
req, err := http.NewRequest(http.MethodGet, API_BASE_URL+"/signercertificate/update", nil)
if err != nil {
return nil, err
}
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("x-resume-token", token)
// Dispatch request TODO: Multi-thread this or cache it
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
// Read response
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Store PEM with appended Headers for x509 parsing
token = resp.Header.Get("x-resume-token")
respKID := resp.Header.Get("x-kid")
kidsMap[respKID] = "-----BEGIN CERTIFICATE-----\n" + string(bodyBytes) + "\n-----END CERTIFICATE-----"
}
return kidsMap, nil
}
// fetchValidKIDs fetches all KIDs from Italian DGC site to compare against Greenpass header
func fetchValidKIDs() ([]string, error) {
resp, err := http.Get(API_BASE_URL + "/signercertificate/status")
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var kids = []string{}
err = json.Unmarshal(bodyBytes, &kids)
if err != nil {
return nil, err
}
return kids, err
}
// extractHeaderBytes extracts header bytes with given tag from Protected or Unprotected header in cose signature object
func extractHeaderBytes(raw *cose.Sign1Message, tag int64) ([]byte, error) {
var dccKid, ok = raw.Headers.Protected[tag]
if !ok {
return extractUnprotectedHeaderBytes(raw, tag)
}
if _, ok := dccKid.([]byte); !ok {
return extractUnprotectedHeaderBytes(raw, tag)
}
return dccKid.([]byte), nil
}
// extractUnprotectedHeaderBytes extracts header bytes with given tag from Unprotected header in cose signature object
func extractUnprotectedHeaderBytes(raw *cose.Sign1Message, tag int64) ([]byte, error) {
var dccKid, ok = raw.Headers.Unprotected[tag]
if !ok {
return nil, fmt.Errorf("tag %d not found in Protected or Unprotected headers", tag)
}
if _, ok = dccKid.([]byte); !ok {
return nil, errors.New("failed to extract KID from Vaccine Passport")
}
return dccKid.([]byte), nil
}