From 1454b58ab2d6a465ff0039ace37c856712a11736 Mon Sep 17 00:00:00 2001 From: AlexPresso Date: Tue, 24 Aug 2021 15:10:00 +0200 Subject: [PATCH] Continue 2DDOC Add Certificate Type enum Add address to struct conversion --- README.md | 2 +- decoders/2ddoc.go | 51 +++++++++++++++++++++--------------------- decoders/dcc.go | 12 ++++++++-- decoders/decoder.go | 3 ++- types/2dcertificate.go | 21 ++++++++++------- types/certificate.go | 13 +++++++++++ types/cose.go | 40 ++++++++++++++++----------------- 7 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 types/certificate.go diff --git a/README.md b/README.md index a62caaa..20034ea 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Golang implementation of the covid certificates. At the moment it only includes ## Features - Decode signed DCC (European QRCodes) data ✅ -- Decode 2D-DOC data ❌ (planned) +- Decode 2D-DOC data ✅ - Pretty-print decoded data as JSON ✅ - Download public-keys from european gateway ❌ (planned) - Verify signature ❌ (planned) diff --git a/decoders/2ddoc.go b/decoders/2ddoc.go index 305aecd..e537e2a 100644 --- a/decoders/2ddoc.go +++ b/decoders/2ddoc.go @@ -1,33 +1,33 @@ package decoders import ( + "encoding/json" "github.com/alexpresso/gocovidcertificate/types" "github.com/alexpresso/gocovidcertificate/utils" "strconv" + "strings" ) var TwoDPrefix = "DC" -func twoDDocDecode(input string) (data *types.TwoDDoc, err error) { +func twoDDocDecode(input string) (certificate *types.Certificate, err error) { header, remaining, err := decodeHeader(input) if err != nil { return nil, err } - message, remaining, err := decodeMessage(remaining) + message, signature, err := decodeData(remaining) if err != nil { return nil, err } - signature, err := decodeSignature(remaining) - if err != nil { - return nil, err - } - - return &types.TwoDDoc{ - Header: *header, - Message: *message, - Signature: *signature, + return &types.Certificate{ + Type: types.TWODDOC, + Data: &types.TwoDDoc{ + Header: header, + Message: message, + Signature: signature, + }, }, nil } @@ -61,25 +61,26 @@ func decodeHeader(input string) (header *types.TwoDDocHeader, remaining string, return header, utils.Substring(input, length, len(input)), err } -func decodeMessage(input string) (message *types.TwoDDocMessage, remaining string, err error) { - remaining = input - data := make(map[string]string) +func decodeData(input string) (message *types.TwoDDocMessage, signature string, err error) { + data := make(map[string]interface{}) - message = &types.TwoDDocMessage{ - Data: data, - } + units := strings.Split(input, string(rune(31))) + groups := strings.Split(units[0], string(rune(29))) + signature = units[1] - return message, remaining, nil -} + for _, group := range groups { + key, value := decodeField(group) + data[key] = value + } -func decodeField(input string) (key string, value string, remaining string, err error) { - key = utils.Substring(input, 0, 2) - value = utils.Substring(input, 2, 4) - remaining = utils.Substring(input, 4, len(input)) + jsonString, err := json.Marshal(data) + err = json.Unmarshal(jsonString, &message) return } -func decodeSignature(input string) (signature *types.TwoDDocSignature, err error) { - return nil, nil +func decodeField(input string) (key string, value string) { + key = utils.Substring(input, 0, 2) + value = utils.Substring(input, 2, len(input)) + return } diff --git a/decoders/dcc.go b/decoders/dcc.go index 516c127..ccbfb78 100644 --- a/decoders/dcc.go +++ b/decoders/dcc.go @@ -8,7 +8,7 @@ import ( var DCCPrefixes = map[string]bool{"HC1": true, "LT1": true} -func dccDecode(input string) (*types.COSE, error) { +func dccDecode(input string) (*types.Certificate, error) { var err error var bytes []byte @@ -28,5 +28,13 @@ func dccDecode(input string) (*types.COSE, error) { } } - return utils.DecodeCOSE(bytes) + cose, err := utils.DecodeCOSE(bytes) + if err != nil { + return nil, err + } + + return &types.Certificate{ + Type: types.DCC, + Data: cose, + }, nil } diff --git a/decoders/decoder.go b/decoders/decoder.go index 17bf00f..7702aba 100644 --- a/decoders/decoder.go +++ b/decoders/decoder.go @@ -2,10 +2,11 @@ package decoders import ( "errors" + "github.com/alexpresso/gocovidcertificate/types" "strings" ) -func Decode(input string) (interface{}, error) { +func Decode(input string) (*types.Certificate, error) { if split := strings.Split(input, ":")[0]; DCCPrefixes[split] { return dccDecode(input) } else if strings.HasPrefix(input, TwoDPrefix) { diff --git a/types/2dcertificate.go b/types/2dcertificate.go index b371efe..01c15cb 100644 --- a/types/2dcertificate.go +++ b/types/2dcertificate.go @@ -1,9 +1,9 @@ package types type TwoDDoc struct { - Header TwoDDocHeader - Message TwoDDocMessage - Signature TwoDDocSignature + Header *TwoDDocHeader + Message *TwoDDocMessage + Signature string } type TwoDDocHeader struct { @@ -19,9 +19,14 @@ type TwoDDocHeader struct { } type TwoDDocMessage struct { - Data map[string]string -} - -type TwoDDocSignature struct { - Raw string + Lastname string `json:"L0"` + Firstname string `json:"L1"` + DateOfBirth string `json:"L2"` + TargetedAgent string `json:"L3"` + VaccineATC string `json:"L4"` + Dose1Manufacturer string `json:"L5"` + Dose2Manufacturer string `json:"L6"` + Dose int `json:"L7,string"` + RequiredDoses int `json:"L8,string"` + Date int `json:"L9,string"` } diff --git a/types/certificate.go b/types/certificate.go new file mode 100644 index 0000000..a5233a4 --- /dev/null +++ b/types/certificate.go @@ -0,0 +1,13 @@ +package types + +const ( + DCC CertificateName = "DCC" + TWODDOC CertificateName = "2DDOC" +) + +type CertificateName string + +type Certificate struct { + Type CertificateName + Data interface{} +} diff --git a/types/cose.go b/types/cose.go index e6b97cc..be9bc86 100644 --- a/types/cose.go +++ b/types/cose.go @@ -2,35 +2,35 @@ package types // Signed see https://datatracker.ietf.org/doc/html/rfc8152#section-2 type Signed struct { - _ struct{} `cbor:",toarray"` - Protected []byte - Unprotected map[interface{}]interface{} - Content []byte - Signature []byte + _ struct{} `cbor:",toarray"` + Protected []byte + Unprotected map[interface{}]interface{} + Content []byte + Signature []byte } // Header see https://datatracker.ietf.org/doc/html/rfc8152#section-3.1 & https://www.iana.org/assignments/cose/cose.xhtml type Header struct { - Alg int `cbor:"1,keyasint,omitempty"` - Kid []byte `cbor:"4,keyasint,omitempty"` - IV []byte `cbor:"5,keyasint,omitempty"` + Alg int `cbor:"1,keyasint,omitempty"` + Kid []byte `cbor:"4,keyasint,omitempty"` + IV []byte `cbor:"5,keyasint,omitempty"` } // Claims see https://datatracker.ietf.org/doc/html/draft-ietf-ace-cbor-web-token-08#section-3.1 type Claims struct { - Iss string `cbor:"1,keyasint"` - Sub string `cbor:"2,keyasint"` - Aud string `cbor:"3,keyasint"` - Exp int64 `cbor:"4,keyasint"` - Nbf int `cbor:"5,keyasint"` - Iat int64 `cbor:"6,keyasint"` - Cti []byte `cbor:"7,keyasint"` - HCData DccRoot `cbor:"-260,keyasint"` - LCData DccRoot `cbor:"-250,keyasint"` + Iss string `cbor:"1,keyasint"` + Sub string `cbor:"2,keyasint"` + Aud string `cbor:"3,keyasint"` + Exp int64 `cbor:"4,keyasint"` + Nbf int `cbor:"5,keyasint"` + Iat int64 `cbor:"6,keyasint"` + Cti []byte `cbor:"7,keyasint"` + HCData DccRoot `cbor:"-260,keyasint"` + LCData DccRoot `cbor:"-250,keyasint"` } type COSE struct { - Signed Signed `json:"-"` - Header Header - Claims Claims + Signed Signed `json:"-"` + Header Header + Claims Claims }