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

Add random padding to responses #490

Merged
merged 2 commits into from
Sep 8, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 41 additions & 14 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
// well as between mobile devices and the server.
package api

import "fmt"
import (
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
)

const (
// TestTypeConfirmed is the string that represents a confirmed covid-19 test.
Expand Down Expand Up @@ -98,13 +103,35 @@ func (e *ErrorReturn) WithCode(code string) *ErrorReturn {
// It's arbitrary bytes that should be ignored or discarded. It primarily exists
// to prevent a network observer from building a model based on request or
// response sizes.
type Padding struct {
Padding string `json:"padding,omitempty"`
type Padding []byte

// MarshalJSON is a custom JSON marshaler for padding. It generates and returns
// 1-2kb (random) of base64-encoded bytes.
func (p Padding) MarshalJSON() ([]byte, error) {
bi, err := rand.Int(rand.Reader, big.NewInt(1024))
if err != nil {
return nil, fmt.Errorf("padding: failed to generate random number: %w", err)
}

// rand.Int is [0, max), so add 1kb to set the range from 1-2kb.
i := int(bi.Int64() + 1024)

b := make([]byte, i)
n, err := rand.Read(b)
if err != nil {
return nil, fmt.Errorf("padding: failed to read bytes: %w", err)
}
if n < i {
return nil, fmt.Errorf("padding: wrote less bytes than expected")
}

s := fmt.Sprintf("%q", base64.StdEncoding.EncodeToString(b))
return []byte(s), nil
}

// CSRFResponse is the return type when requesting an AJAX CSRF token.
type CSRFResponse struct {
Padding
Padding Padding `json:"padding"`

CSRFToken string `json:"csrftoken"`
Error string `json:"error"`
Expand All @@ -115,7 +142,7 @@ type CSRFResponse struct {
// code. This is called by the Web frontend.
// API is served at /api/issue
type IssueCodeRequest struct {
Padding
Padding Padding `json:"padding"`

SymptomDate string `json:"symptomDate"` // ISO 8601 formatted date, YYYY-MM-DD
TestDate string `json:"testDate"`
Expand All @@ -128,7 +155,7 @@ type IssueCodeRequest struct {

// IssueCodeResponse defines the response type for IssueCodeRequest.
type IssueCodeResponse struct {
Padding
Padding Padding `json:"padding"`

// UUID is a handle which allows the issuer to track status of the issued verification code.
UUID string `json:"uuid"`
Expand Down Expand Up @@ -157,15 +184,15 @@ type IssueCodeResponse struct {
// previously issued OTP code. This is called by the Web frontend.
// API is served at /api/checkcodestatus
type CheckCodeStatusRequest struct {
Padding
Padding Padding `json:"padding"`

// UUID is a handle which allows the issuer to track status of the issued verification code.
UUID string `json:"uuid"`
}

// CheckCodeStatusResponse defines the response type for CheckCodeStatusRequest.
type CheckCodeStatusResponse struct {
Padding
Padding Padding `json:"padding"`

// Claimed is true if a user has used the OTP code to get a token via the VerifyCode api.
Claimed bool `json:"claimed"`
Expand All @@ -186,15 +213,15 @@ type CheckCodeStatusResponse struct {
// This is called by the Web frontend.
// API is served at /api/expirecode
type ExpireCodeRequest struct {
Padding
Padding Padding `json:"padding"`

// UUID is a handle which allows the issuer to track status of the issued verification code.
UUID string `json:"uuid"`
}

// ExpireCodeResponse defines the response type for ExpireCodeRequest.
type ExpireCodeResponse struct {
Padding
Padding Padding `json:"padding"`

// ExpiresAtTimestamp represents Unix, seconds since the epoch. Still UTC.
// After this time the code will no longer be accepted and is eligible for deletion.
Expand Down Expand Up @@ -226,7 +253,7 @@ type ExpireCodeResponse struct {
//
// Requires API key in a HTTP header, X-API-Key: APIKEY
type VerifyCodeRequest struct {
Padding
Padding Padding `json:"padding"`

VerificationCode string `json:"code"`
AcceptTestTypes []string `json:"accept"`
Expand All @@ -236,7 +263,7 @@ type VerifyCodeRequest struct {
// (type and [optional] date) as well as the verification token. The verification token
// may be sent back on a valid VerificationCertificateRequest later.
type VerifyCodeResponse struct {
Padding
Padding Padding `json:"padding"`

TestType string `json:"testtype,omitempty"`
SymptomDate string `json:"symptomDate,omitempty"` // ISO 8601 formatted date, YYYY-MM-DD
Expand All @@ -252,7 +279,7 @@ type VerifyCodeResponse struct {
//
// Requires API key in a HTTP header, X-API-Key: APIKEY
type VerificationCertificateRequest struct {
Padding
Padding Padding `json:"padding"`

VerificationToken string `json:"token"`
ExposureKeyHMAC string `json:"ekeyhmac"`
Expand All @@ -262,7 +289,7 @@ type VerificationCertificateRequest struct {
// a signed certificate that can be presented to the configured exposure
// notifications server to publish keys along w/ the certified diagnosis.
type VerificationCertificateResponse struct {
Padding
Padding Padding `json:"padding"`

Certificate string `json:"certificate,omitempty"`
Error string `json:"error,omitempty"`
Expand Down