package handlers import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "time" ) // blobUploadState captures the state serializable state of the blob upload. type blobUploadState struct { // name is the primary repository under which the blob will be linked. Name string // UUID identifies the upload. UUID string // offset contains the current progress of the upload. Offset int64 // StartedAt is the original start time of the upload. StartedAt time.Time } type hmacKey string var errInvalidSecret = fmt.Errorf("invalid secret") // unpackUploadState unpacks and validates the blob upload state from the // token, using the hmacKey secret. func (secret hmacKey) unpackUploadState(token string) (blobUploadState, error) { var state blobUploadState tokenBytes, err := base64.URLEncoding.DecodeString(token) if err != nil { return state, err } mac := hmac.New(sha256.New, []byte(secret)) if len(tokenBytes) < mac.Size() { return state, errInvalidSecret } macBytes := tokenBytes[:mac.Size()] messageBytes := tokenBytes[mac.Size():] mac.Write(messageBytes) if !hmac.Equal(mac.Sum(nil), macBytes) { return state, errInvalidSecret } if err := json.Unmarshal(messageBytes, &state); err != nil { return state, err } return state, nil } // packUploadState packs the upload state signed with and hmac digest using // the hmacKey secret, encoding to url safe base64. The resulting token can be // used to share data with minimized risk of external tampering. func (secret hmacKey) packUploadState(lus blobUploadState) (string, error) { mac := hmac.New(sha256.New, []byte(secret)) p, err := json.Marshal(lus) if err != nil { return "", err } mac.Write(p) return base64.URLEncoding.EncodeToString(append(mac.Sum(nil), p...)), nil }