mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-06-20 04:04:21 -04:00
api: refine signature types
Wrapping apiObject does not work as intended as the version field is when fetching objects from the API. Thus we need to insert the target path of the signature directly.
This commit is contained in:
parent
7ffa1344e3
commit
2b19632e09
7 changed files with 81 additions and 25 deletions
|
@ -20,9 +20,10 @@ import (
|
||||||
// newDeleteCmd creates the delete command.
|
// newDeleteCmd creates the delete command.
|
||||||
func newDeleteCmd() *cobra.Command {
|
func newDeleteCmd() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "delete",
|
Use: "delete",
|
||||||
Short: "delete a specific version from the config api",
|
Short: "delete a specific version from the config api",
|
||||||
RunE: runDelete,
|
PreRunE: envCheck,
|
||||||
|
RunE: runDelete,
|
||||||
}
|
}
|
||||||
cmd.Flags().StringP("version", "v", "", "Name of the version to delete (without .json suffix)")
|
cmd.Flags().StringP("version", "v", "", "Name of the version to delete (without .json suffix)")
|
||||||
must(cmd.MarkFlagRequired("version"))
|
must(cmd.MarkFlagRequired("version"))
|
||||||
|
|
|
@ -21,9 +21,9 @@ import (
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/staticupload"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/staticupload"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||||
|
@ -266,7 +266,7 @@ func SignAndUpdate(ctx context.Context, c *Client, obj APIObject, signer sigstor
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := signature{
|
signature := signature{
|
||||||
Signed: obj,
|
Signed: obj.JSONPath(),
|
||||||
Signature: dataSignature,
|
Signature: dataSignature,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ func DeleteWithSignature(ctx context.Context, c *Client, obj APIObject) error {
|
||||||
return fmt.Errorf("deleting %T: %w", obj, err)
|
return fmt.Errorf("deleting %T: %w", obj, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := signature{Signed: obj}
|
sig := signature{Signed: obj.JSONPath()}
|
||||||
if err := Delete(ctx, c, sig); err != nil {
|
if err := Delete(ctx, c, sig); err != nil {
|
||||||
return fmt.Errorf("deleting %T: %w", sig, err)
|
return fmt.Errorf("deleting %T: %w", sig, err)
|
||||||
}
|
}
|
||||||
|
@ -350,25 +350,29 @@ type uploadClient interface {
|
||||||
// CloseFunc is a function that closes the client.
|
// CloseFunc is a function that closes the client.
|
||||||
type CloseFunc func(ctx context.Context) error
|
type CloseFunc func(ctx context.Context) error
|
||||||
|
|
||||||
// signature wraps another APIObject and adds a signature to it.
|
// signature manages the signature of a object saved at location 'Signed'.
|
||||||
type signature struct {
|
type signature struct {
|
||||||
// Signed is the object that is signed.
|
// Signed is the object that is signed.
|
||||||
Signed APIObject
|
Signed string `json:"signed"`
|
||||||
// Signature is the signature of `Signed`.
|
// Signature is the signature of `Signed`.
|
||||||
Signature []byte `json:"signature"`
|
Signature []byte `json:"signature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||||
func (s signature) JSONPath() string {
|
func (s signature) JSONPath() string {
|
||||||
return path.Join(s.Signed.JSONPath() + ".sig")
|
return s.Signed + ".sig"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRequest validates the request.
|
// ValidateRequest validates the request.
|
||||||
func (s signature) ValidateRequest() error {
|
func (s signature) ValidateRequest() error {
|
||||||
return s.Signed.ValidateRequest()
|
if !strings.HasSuffix(s.Signed, ".json") {
|
||||||
|
return errors.New("signed object missing .json suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate is a No-Op at the moment.
|
// Validate checks that the signature is base64 encoded.
|
||||||
func (s signature) Validate() error {
|
func (s signature) Validate() error {
|
||||||
return s.Signed.Validate()
|
return sigstore.IsBase64([]byte(s.Signature))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,8 @@ go_library(
|
||||||
srcs = ["fetcher.go"],
|
srcs = ["fetcher.go"],
|
||||||
importpath = "github.com/edgelesssys/constellation/v2/internal/api/fetcher",
|
importpath = "github.com/edgelesssys/constellation/v2/internal/api/fetcher",
|
||||||
visibility = ["//:__subpackages__"],
|
visibility = ["//:__subpackages__"],
|
||||||
deps = ["//internal/sigstore"],
|
deps = [
|
||||||
|
"//internal/constants",
|
||||||
|
"//internal/sigstore",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,9 +20,12 @@ package fetcher
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,7 +90,11 @@ func FetchAndVerify[T apiObject](ctx context.Context, c HTTPClient, obj T, cosig
|
||||||
return fetchedObj, fmt.Errorf("marshalling object: %w", err)
|
return fetchedObj, fmt.Errorf("marshalling object: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := Fetch(ctx, c, signature{Signed: fetchedObj})
|
url, err := obj.URL()
|
||||||
|
if err != nil {
|
||||||
|
return fetchedObj, fmt.Errorf("getting signed URL: %w", err)
|
||||||
|
}
|
||||||
|
signature, err := Fetch(ctx, c, signature{Signed: url})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fetchedObj, fmt.Errorf("fetching signature: %w", err)
|
return fetchedObj, fmt.Errorf("fetching signature: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -123,29 +130,33 @@ type apiObject interface {
|
||||||
URL() (string, error)
|
URL() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// signature wraps another APIObject and adds a signature to it.
|
// signature manages the signature of a object saved at location 'Signed'.
|
||||||
type signature struct {
|
type signature struct {
|
||||||
// Signed is the object that is signed.
|
// Signed is the object that is signed.
|
||||||
Signed apiObject `json:"-"`
|
Signed string `json:"signed"`
|
||||||
// Signature is the signature of `Signed`.
|
// Signature is the signature of `Signed`.
|
||||||
Signature []byte `json:"signature"`
|
Signature []byte `json:"signature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL returns the URL for the request to the config api.
|
// URL returns the URL for the request to the config api.
|
||||||
func (s signature) URL() (string, error) {
|
func (s signature) URL() (string, error) {
|
||||||
url, err := s.Signed.URL()
|
return s.Signed + ".sig", nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return url + ".sig", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRequest validates the request.
|
// ValidateRequest validates the request.
|
||||||
func (s signature) ValidateRequest() error {
|
func (s signature) ValidateRequest() error {
|
||||||
return s.Signed.ValidateRequest()
|
if !strings.HasPrefix(s.Signed, constants.CDNRepositoryURL) {
|
||||||
|
return errors.New("signed object missing CDN URL prefix")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(s.Signed, ".json") {
|
||||||
|
return errors.New("signed object missing .json suffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate is a No-Op at the moment.
|
// Validate checks that the signature is base64 encoded.
|
||||||
func (s signature) Validate() error {
|
func (s signature) Validate() error {
|
||||||
return s.Signed.Validate()
|
return sigstore.IsBase64(s.Signature)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,3 +63,10 @@ func (c CosignVerifier) VerifySignature(content, signature []byte) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBase64 checks if the given byte slice is base64 encoded.
|
||||||
|
func IsBase64(signature []byte) error {
|
||||||
|
target := make([]byte, base64.StdEncoding.DecodedLen(len(signature)))
|
||||||
|
_, err := base64.StdEncoding.Decode(target, signature)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -89,3 +89,33 @@ gCDlEzkuOCybCHf+q766bve799L7Y5y5oRsHY1MrUCUwYF/tL7Sg7EYMsA==
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsBase64(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
signature []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"valid base64": {
|
||||||
|
signature: []byte("SGVsbG8gV29ybGQ="),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
"invalid base64": {
|
||||||
|
signature: []byte("not base64"),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"empty input": {
|
||||||
|
signature: []byte{},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tc, tt := range tests {
|
||||||
|
t.Run(tc, func(t *testing.T) {
|
||||||
|
err := IsBase64(tt.signature)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("IsBase64() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue