Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
V1.5export (#722)
Browse files Browse the repository at this point in the history
* v1.5 export files

* Add new TEK metadata fields to generated exports
* Add revised keys to output

Work on #663

* fix build errors

* fix out of bounds w/ empty data

* comments on new errors
  • Loading branch information
mikehelmick authored Jul 15, 2020
1 parent 583290b commit 821531b
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 129 deletions.
70 changes: 54 additions & 16 deletions internal/export/exportfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/google/exposure-notifications-server/internal/export/model"
publishmodel "github.com/google/exposure-notifications-server/internal/publish/model"
verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1alpha1"

"github.com/google/exposure-notifications-server/internal/pb/export"

Expand All @@ -51,9 +52,9 @@ type Signer struct {
}

// MarshalExportFile converts the inputs into an encoded byte array.
func MarshalExportFile(eb *model.ExportBatch, exposures []*publishmodel.Exposure, batchNum, batchSize int, signers []*Signer) ([]byte, error) {
func MarshalExportFile(eb *model.ExportBatch, exposures, revisedExposures []*publishmodel.Exposure, batchNum, batchSize int, signers []*Signer) ([]byte, error) {
// create main exposure key export binary
expContents, err := marshalContents(eb, exposures, int32(batchNum), int32(batchSize), signers)
expContents, err := marshalContents(eb, exposures, revisedExposures, int32(batchNum), int32(batchSize), signers)
if err != nil {
return nil, fmt.Errorf("unable to marshal exposure keys: %w", err)
}
Expand Down Expand Up @@ -131,30 +132,66 @@ func unmarshalContent(file *zip.File) (*export.TemporaryExposureKeyExport, error
return message, nil
}

func marshalContents(eb *model.ExportBatch, exposures []*publishmodel.Exposure, batchNum int32, batchSize int32, signers []*Signer) ([]byte, error) {
func sortExposures(exposures []*publishmodel.Exposure) {
sort.Slice(exposures, func(i, j int) bool {
return bytes.Compare(exposures[i].ExposureKey, exposures[j].ExposureKey) < 0
})
}

func makeTEK(exp *publishmodel.Exposure) *export.TemporaryExposureKey {
pbek := export.TemporaryExposureKey{
KeyData: exp.ExposureKey,
TransmissionRiskLevel: proto.Int32(int32(exp.TransmissionRisk)),
}
if exp.IntervalNumber != 0 {
pbek.RollingStartIntervalNumber = proto.Int32(exp.IntervalNumber)
}
if exp.IntervalCount != defaultIntervalCount {
pbek.RollingPeriod = proto.Int32(exp.IntervalCount)
}
return &pbek
}

func assignReportType(reportType *string, pbek *export.TemporaryExposureKey) {
if reportType == nil {
return
}
switch *reportType {
case verifyapi.ReportTypeConfirmed:
pbek.ReportType = export.TemporaryExposureKey_CONFIRMED_TEST.Enum()
case verifyapi.ReportTypeClinical:
pbek.ReportType = export.TemporaryExposureKey_CONFIRMED_CLINICAL_DIAGNOSIS.Enum()
case verifyapi.ReportTypeNegative:
pbek.ReportType = export.TemporaryExposureKey_REVOKED.Enum()
}
}

func marshalContents(eb *model.ExportBatch, exposures, revisedExposures []*publishmodel.Exposure, batchNum int32, batchSize int32, signers []*Signer) ([]byte, error) {
exportBytes := fixedHeader
if len(exportBytes) != fixedHeaderWidth {
return nil, fmt.Errorf("incorrect header length: %d", len(exportBytes))
}
// We want to scramble keys to ensure no associations, so arbitrarily sort them.
// This could be done at the db layer but doing it here makes it explicit that its
// important to the serialization
sort.Slice(exposures, func(i, j int) bool {
return bytes.Compare(exposures[i].ExposureKey, exposures[j].ExposureKey) < 0
})
sortExposures(exposures)
var pbeks []*export.TemporaryExposureKey
for _, exp := range exposures {
pbek := export.TemporaryExposureKey{
KeyData: exp.ExposureKey,
TransmissionRiskLevel: proto.Int32(int32(exp.TransmissionRisk)),
}
if exp.IntervalNumber != 0 {
pbek.RollingStartIntervalNumber = proto.Int32(exp.IntervalNumber)
}
if exp.IntervalCount != defaultIntervalCount {
pbek.RollingPeriod = proto.Int32(exp.IntervalCount)
pbek := makeTEK(exp)
assignReportType(&exp.ReportType, pbek)
if exp.HasDaysSinceSymptomOnset() {
pbek.DaysSinceOnsetOfSymptoms = proto.Int32(*exp.DaysSinceSymptomOnset)
}
pbeks = append(pbeks, &pbek)
pbeks = append(pbeks, pbek)
}

sortExposures(revisedExposures)
var pbRevisedKeys []*export.TemporaryExposureKey
for _, exp := range revisedExposures {
pbek := makeTEK(exp)
assignReportType(exp.RevisedReportType, pbek)
pbek.DaysSinceOnsetOfSymptoms = exp.RevisedDaysSinceSymptomOnset
pbRevisedKeys = append(pbRevisedKeys, pbek)
}

var exportSigInfos []*export.SignatureInfo
Expand All @@ -169,6 +206,7 @@ func marshalContents(eb *model.ExportBatch, exposures []*publishmodel.Exposure,
BatchNum: proto.Int32(batchNum),
BatchSize: proto.Int32(batchSize),
Keys: pbeks,
RevisedKeys: pbRevisedKeys,
SignatureInfos: exportSigInfos,
}
protoBytes, err := proto.Marshal(&pbeke)
Expand Down
62 changes: 47 additions & 15 deletions internal/export/exportfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/google/exposure-notifications-server/internal/export/model"
"github.com/google/exposure-notifications-server/internal/pb/export"
publishmodel "github.com/google/exposure-notifications-server/internal/publish/model"
verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1alpha1"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/proto"
Expand All @@ -47,22 +49,39 @@ func TestMarshalUnmarshalExportFile(t *testing.T) {

exposures := []*publishmodel.Exposure{
{
ExposureKey: []byte("ABC"),
Regions: []string{"US"},
IntervalNumber: 18,
IntervalCount: 0,
CreatedAt: batchStartTime,
LocalProvenance: true,
TransmissionRisk: 8,
ExposureKey: []byte("ABC"),
Regions: []string{"US"},
IntervalNumber: 18,
IntervalCount: 0,
CreatedAt: batchStartTime,
LocalProvenance: true,
TransmissionRisk: 8,
DaysSinceSymptomOnset: proto.Int32(0),
ReportType: verifyapi.ReportTypeClinical,
},
{
ExposureKey: []byte("DEF"),
Regions: []string{"CA"},
IntervalNumber: 118,
IntervalCount: 1,
CreatedAt: batchEndTime,
LocalProvenance: true,
TransmissionRisk: 1,
DaysSinceSymptomOnset: proto.Int32(-1),
ReportType: verifyapi.ReportTypeConfirmed,
},
}
revisedExposures := []*publishmodel.Exposure{
{
ExposureKey: []byte("DEF"),
Regions: []string{"CA"},
IntervalNumber: 118,
IntervalCount: 1,
CreatedAt: batchEndTime,
LocalProvenance: true,
TransmissionRisk: 1,
ExposureKey: []byte("123"),
Regions: []string{"US"},
IntervalNumber: 100,
IntervalCount: 144,
CreatedAt: batchStartTime,
LocalProvenance: true,
TransmissionRisk: 4,
DaysSinceSymptomOnset: proto.Int32(2),
RevisedReportType: proto.String(verifyapi.ReportTypeNegative),
},
}

Expand All @@ -75,7 +94,7 @@ func TestMarshalUnmarshalExportFile(t *testing.T) {

signer := &customTestSigner{}

blob, err := MarshalExportFile(batch, exposures, 1, 1, []*Signer{
blob, err := MarshalExportFile(batch, exposures, revisedExposures, 1, 1, []*Signer{
{SignatureInfo: signatureInfo, Signer: signer},
})
if err != nil {
Expand All @@ -101,12 +120,24 @@ func TestMarshalUnmarshalExportFile(t *testing.T) {
TransmissionRiskLevel: proto.Int32(8),
RollingStartIntervalNumber: proto.Int32(18),
RollingPeriod: proto.Int32(0),
ReportType: export.TemporaryExposureKey_CONFIRMED_CLINICAL_DIAGNOSIS.Enum(),
DaysSinceOnsetOfSymptoms: proto.Int32(0),
},
{
KeyData: []byte("DEF"),
TransmissionRiskLevel: proto.Int32(1),
RollingStartIntervalNumber: proto.Int32(118),
RollingPeriod: proto.Int32(1),
ReportType: export.TemporaryExposureKey_CONFIRMED_TEST.Enum(),
DaysSinceOnsetOfSymptoms: proto.Int32(-1),
},
}
revisedKeys := []*export.TemporaryExposureKey{
{
KeyData: []byte("123"),
TransmissionRiskLevel: proto.Int32(4),
RollingStartIntervalNumber: proto.Int32(100),
ReportType: export.TemporaryExposureKey_REVOKED.Enum(),
},
}

Expand All @@ -118,6 +149,7 @@ func TestMarshalUnmarshalExportFile(t *testing.T) {
BatchSize: proto.Int32(1),
SignatureInfos: infos,
Keys: keys,
RevisedKeys: revisedKeys,
}

ignoredTemporaryExposureKeyExportFields := cmpopts.IgnoreUnexported(export.TemporaryExposureKeyExport{})
Expand Down
Loading

0 comments on commit 821531b

Please sign in to comment.