Skip to content

Commit

Permalink
Formatting improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
reznik99 committed Dec 8, 2021
1 parent e3f766b commit ab642b6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 76 deletions.
44 changes: 25 additions & 19 deletions dcc-generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,27 @@ import (
// TODO: Temporary hardcoded Key ID
var newZealandKID4 = "dy8HnMQYOrE="

func GenerateGreenpass(inputPath string, outputPath string, outputType int) {
func GenerateQR(inputPath string, outputPath string) error {

raw, err := Generate(inputPath, outputPath)
if err != nil {
return err
}

// Convert to QR Code or raw txt
err = qrcode.WriteFile(raw, qrcode.Medium, 256, outputPath)
if err != nil {
return err
}

return nil
}

func Generate(inputPath string, outputPath string) (dccBase54 string, err error) {

conf, err := readRaw(inputPath)
if err != nil {
panic(err)
return
}

// Generate JSON eu-dcc structure
Expand All @@ -31,17 +47,17 @@ func GenerateGreenpass(inputPath string, outputPath string, outputType int) {
// JSON to CBOR
dccCBORBytes, err := cbor.Marshal(dccJson)
if err != nil {
panic(err)
return
}

// Sign CBOR with COSE
dccCOSESignMsg, err := coseSign(dccCBORBytes)
if err != nil {
panic(err)
return
}
dccCOSE, err := dccCOSESignMsg.MarshalCBOR()
if err != nil {
panic(err)
return
}

// Compress Binary COSE Data with zlib
Expand All @@ -53,21 +69,11 @@ func GenerateGreenpass(inputPath string, outputPath string, outputType int) {
// Prepend magic HC1 (Health Certificate Version 1)
dccBase45 = fmt.Sprintf("HC1:%s", dccBase45)

fmt.Println(dccBase45)

// Convert to QR Code or raw txt
switch outputType {
case TypeRAWGreepass:
err = ioutil.WriteFile(outputPath, []byte(dccBase45), 0644)
if err != nil {
panic(err)
}
case TypeQRCode:
err = qrcode.WriteFile(dccBase45, qrcode.Medium, 256, outputPath)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(outputPath, []byte(dccBase45), 0644)
if err != nil {
return
}
return
}

func coseSign(data []byte) (*cose.Sign1Message, error) {
Expand Down
19 changes: 10 additions & 9 deletions dcc-parser.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dcc

import (
"fmt"
"errors"
"io/ioutil"
"strings"

Expand All @@ -10,12 +10,13 @@ import (
"github.com/veraison/go-cose"
)

func ParseGreenpass(path string, fileType int) (payload *DCC, messageRaw *cose.Sign1Message, err error) {
var dccPrefix = "HC1:"

if fileType == TypeQRCode {
err = fmt.Errorf("QRCode parsing not yet implemented")
return
}
func ParseQR(path string) error {
return errors.New("QRCode parsing not yet implemented")
}

func Parse(path string) (payload *DCC, messageRaw *cose.Sign1Message, err error) {

// Read vaccine pass
fileBytes, err := ioutil.ReadFile(path)
Expand All @@ -25,11 +26,11 @@ func ParseGreenpass(path string, fileType int) (payload *DCC, messageRaw *cose.S

// remove magic header : HC1: (Health Certificate Version 1)
dccBase45 := string(fileBytes)
if !strings.HasPrefix(dccBase45, "HC1:") {
err = fmt.Errorf("data does not appear to be EU DCC / Vaccine Passport")
if !strings.HasPrefix(dccBase45, dccPrefix) {
err = errors.New("data does not appear to be EU DCC / Vaccine Passport")
return
}
dccBase45 = strings.Split(dccBase45, "HC1:")[1]
dccBase45 = strings.Split(dccBase45, dccPrefix)[1]

// Decode base45
dccCOSECompressed, err := base45.DecodeString(dccBase45)
Expand Down
86 changes: 38 additions & 48 deletions dcc-validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,54 @@ import (
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"log"
"net/http"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/veraison/go-cose"
)

var (
API_BASE_URL = "https://get.dgc.gov.it/v1/dgc"
)

func VerifyGreenpass(filePath string, fileType int) {
// Verify verifies a Vaccine Passport, it reads the file of type fileType
func Verify(filePath string) (valid bool, err error) {
// Parse certificate from raw or QR code, returning raw cose and parsed dcc payload
dcc, raw, err := ParseGreenpass(filePath, fileType)
_, raw, err := Parse(filePath)
if err != nil {
log.Fatalf("%s\n", err.Error())
return
}

// Fetch KIDs to compare
kidsList := fetchValidKIDs()
fmt.Printf("Fetched %d KIDs from %s\n", len(kidsList), API_BASE_URL)
kidsList, err := fetchValidKIDs()
if err != nil {
return
}

// Write PEMs into KIDs map
// kids := map[string]string{}
// kids["dy8HnMQYOrE="] = "-----BEGIN CERTIFICATE-----\nMIICmDCCAh6gAwIBAgIUNe+sDjaZPIfOw8OIMwqniNZ1ABEwCgYIKoZIzj0EAwIwZTELMAkGA1UEBhMCTloxIjAgBgNVBAoMGUdvdmVybm1lbnQgb2YgTmV3IFplYWxhbmQxGzAZBgNVBAsMEk1pbmlzdHJ5IG9mIEhlYWx0aDEVMBMGA1UEAwwMVmFjY2luZSBDU0NBMB4XDTIxMTEwNDAxMDMwOFoXDTMyMDMwMTAxMDMwN1owgZQxCzAJBgNVBAYTAk5aMSIwIAYDVQQKDBlHb3Zlcm5tZW50IG9mIE5ldyBaZWFsYW5kMRswGQYDVQQLDBJNaW5pc3RyeSBvZiBIZWFsdGgxFTATBgNVBAsMDFZhY2NpbmUgQ1NDQTEtMCsGA1UEAwwkVmFjY2luZSBEb2N1bWVudCBTaWduZXIgMjAyMTExMDIwMDEwMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQEqBzWkYP17/i2b+EPcLpiQvTKLxasVDjA3A7IvRt9RHjYFIQGpFQCR7ZJLD5XQNsjcFfHzxwVEITkb5M7fIyaN8MHowHwYDVR0jBBgwFoAUBYexBS6L22zGjlutPHLVuaircxkwHQYDVR0OBBYEFIrSwiSmYianLXQsxR6ZAFL5/ltIMCsGA1UdEAQkMCKADzIwMjExMTA0MDEwMzA4WoEPMjAyMjAzMDQwMTAzMDhaMAsGA1UdDwQEAwIHgDAKBggqhkjOPQQDAgNoADBlAjEA9UwmICswUwNiWRzvb4V+U0Z7qKebbIIldgtTp+nHmcme5HGjKc8UuT/yvuzzK4qoAjBvlH+kAQGZuXrSXduh+CtY20W+NEPrYV6bjDUdxQEzCxmOrsA1LWl2lHdIcLRSDdc=\n-----END CERTIFICATE-----"
kids := fetchCerts(kidsList)
kids, err := fetchCerts(kidsList)
if err != nil {
return
}

// Extract KID from Passports Header
kidBytes, err := ExtractHeaderBytes(raw, "kid")
kidBytes, err := extractHeaderBytes(raw, "kid")
if err != nil {
log.Fatalf("%s\n", err.Error())
return
}
var kid = base64.StdEncoding.EncodeToString(kidBytes)

// Check KID is in trusted list
if _, ok := kids[kid]; !ok {
log.Fatalf("vaccine Pass KID %s not found on trusted list at: %s\n", kid, API_BASE_URL)
return
}
fmt.Printf("Vaccine Pass KID '%s' is trusted\n", kid)

// Parse Signer certificate belonging to KID. Extract public key
pemCertificate := kids[kid]
fmt.Printf("Vaccine Pass Signer Certificate PEM: \n%s\n", pemCertificate)
block, _ := pem.Decode([]byte(pemCertificate))
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Fatalf("%s\n", err.Error())
return
}

publicKey := cert.PublicKey.(crypto.PublicKey)
Expand All @@ -65,93 +64,84 @@ func VerifyGreenpass(filePath string, fileType int) {
toBeSigned, _ := raw.SigStructure(nil)
digest := sha256.Sum256(toBeSigned)

fmt.Printf("Digest: %s\n", hexutil.Encode(digest[:]))
fmt.Printf("Signature: %s\n", hexutil.Encode(raw.Signature))

verifier := cose.Verifier{
PublicKey: publicKey,
Alg: cose.ES256,
}

// Verify the Vaccine Passport's Signature
err = verifier.Verify(digest[:], raw.Signature)
if err != nil {
err = raw.Verify(nil, verifier)
if err != nil {
fmt.Printf("Verification FAILED with err: %s\n", err.Error())
} else {
fmt.Printf("%s's Greenpass Validated succesfully\n", dcc.HealthCertificate.DGC.Nam.Gn)
}
} else {
fmt.Printf("%s's Greenpass Validated succesfully\n", dcc.HealthCertificate.DGC.Nam.Gn)
return false, nil
}
return true, nil
}

// fetchCerts fetches all the Signer Certificates to be used to validate International Vaccine Passports
func fetchCerts(kids []string) (kidsMap map[string]string) {
kidsMap = map[string]string{}
func fetchCerts(kids []string) (map[string]string, error) {
var kidsMap = map[string]string{}
var token = "0"
for idx := range kids {

for range kids {
// Generate request wit headers
req, err := http.NewRequest(http.MethodGet, API_BASE_URL+"/signercertificate/update", nil)
if err != nil {
panic(err)
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 {
panic(err)
return nil, err
}
// Read response
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
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-----"

fmt.Printf("\rFetched %d/%d Signer Certificates", idx, len(kids))
}
fmt.Printf("\rFetched %d/%d Signer Certificates\n", len(kids), len(kids))
return

return kidsMap, nil
}

// fetchValidKIDs fetches all KIDs from Italian DGC site to compare against Greenpass header
func fetchValidKIDs() []string {
func fetchValidKIDs() ([]string, error) {
resp, err := http.Get(API_BASE_URL + "/signercertificate/status")
if err != nil {
panic(err)
return nil, err
}
defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
return nil, err
}

kids := []string{}
var kids = []string{}
err = json.Unmarshal(bodyBytes, &kids)
if err != nil {
panic(err)
return nil, err
}

return kids
return kids, err
}

// ExtractHeaderBytes extracts header bytes with given tag name, from Protected or Unprotected header in cose signature object
func ExtractHeaderBytes(raw *cose.Sign1Message, headerName string) ([]byte, error) {
tag, err := cose.GetCommonHeaderTag(headerName)
// extractHeaderBytes extracts header bytes with given tag name, from Protected or Unprotected header in cose signature object
func extractHeaderBytes(raw *cose.Sign1Message, headerName string) ([]byte, error) {
var tag, err = cose.GetCommonHeaderTag(headerName)
if err != nil {
return nil, err
}

dccKid := raw.Headers.Protected[tag]
var dccKid = raw.Headers.Protected[tag]
if _, ok := dccKid.([]byte); !ok {
dccKid = raw.Headers.Unprotected[tag]
if _, ok = dccKid.([]byte); !ok {
Expand Down

0 comments on commit ab642b6

Please sign in to comment.