2023-05-25 12:43:44 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2023-06-07 10:16:32 -04:00
package attestationconfigapi
2023-05-25 12:43:44 -04:00
import (
"context"
"encoding/json"
"fmt"
"time"
2023-06-05 06:33:22 -04:00
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
2023-06-09 09:41:02 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
2023-06-05 06:33:22 -04:00
"github.com/edgelesssys/constellation/v2/internal/logger"
2023-06-01 07:55:46 -04:00
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/edgelesssys/constellation/v2/internal/staticupload"
2023-05-25 12:43:44 -04:00
)
2023-06-12 10:04:54 -04:00
// VersionFormat is the format of the version name in the S3 bucket.
const VersionFormat = "2006-01-02-15-04"
2023-06-02 03:19:23 -04:00
// Client manages (modifies) the version information for the attestation variants.
type Client struct {
2023-06-05 06:33:22 -04:00
s3Client * apiclient . Client
2023-06-02 05:20:01 -04:00
s3ClientClose func ( ctx context . Context ) error
bucketID string
2023-06-05 06:33:22 -04:00
signer sigstore . Signer
2023-05-25 12:43:44 -04:00
}
2023-06-07 10:16:32 -04:00
// NewClient returns a new Client.
func NewClient ( ctx context . Context , cfg staticupload . Config , cosignPwd , privateKey [ ] byte , dryRun bool , log * logger . Logger ) ( * Client , apiclient . CloseFunc , error ) {
2023-06-05 06:33:22 -04:00
s3Client , clientClose , err := apiclient . NewClient ( ctx , cfg . Region , cfg . Bucket , cfg . DistributionID , dryRun , log )
2023-05-25 12:43:44 -04:00
if err != nil {
2023-06-02 05:20:01 -04:00
return nil , nil , fmt . Errorf ( "failed to create s3 storage: %w" , err )
2023-05-25 12:43:44 -04:00
}
2023-06-05 06:33:22 -04:00
2023-06-02 05:20:01 -04:00
repo := & Client {
2023-06-05 06:33:22 -04:00
s3Client : s3Client ,
2023-06-02 05:20:01 -04:00
s3ClientClose : clientClose ,
2023-06-05 06:33:22 -04:00
signer : sigstore . NewSigner ( cosignPwd , privateKey ) ,
2023-06-02 05:20:01 -04:00
bucketID : cfg . Bucket ,
}
2023-06-05 06:33:22 -04:00
return repo , clientClose , nil
2023-06-02 05:20:01 -04:00
}
2023-06-09 06:48:12 -04:00
// UploadAzureSEVSNP uploads the latest version numbers of the Azure SEVSNP. Then version name is the UTC timestamp of the date. The /list entry stores the version name + .json suffix.
2023-06-07 10:16:32 -04:00
func ( a Client ) UploadAzureSEVSNP ( ctx context . Context , version AzureSEVSNPVersion , date time . Time ) error {
2023-06-05 10:10:44 -04:00
versions , err := a . List ( ctx , variant . AzureSEVSNP { } )
2023-06-01 07:55:46 -04:00
if err != nil {
2023-06-05 10:10:44 -04:00
return fmt . Errorf ( "fetch version list: %w" , err )
2023-06-01 07:55:46 -04:00
}
2023-06-05 10:10:44 -04:00
ops , err := a . uploadAzureSEVSNP ( version , versions , date )
2023-05-25 12:43:44 -04:00
if err != nil {
return err
}
2023-06-05 10:10:44 -04:00
return executeAllCmds ( ctx , a . s3Client , ops )
2023-06-05 06:33:22 -04:00
}
2023-06-05 10:10:44 -04:00
// DeleteAzureSEVSNPVersion deletes the given version (without .json suffix) from the API.
func ( a Client ) DeleteAzureSEVSNPVersion ( ctx context . Context , versionStr string ) error {
versions , err := a . List ( ctx , variant . AzureSEVSNP { } )
2023-06-05 06:33:22 -04:00
if err != nil {
2023-06-05 10:10:44 -04:00
return fmt . Errorf ( "fetch version list: %w" , err )
2023-06-05 06:33:22 -04:00
}
2023-06-05 10:10:44 -04:00
ops , err := a . deleteAzureSEVSNPVersion ( versions , versionStr )
2023-06-01 07:55:46 -04:00
if err != nil {
2023-06-05 06:33:22 -04:00
return err
2023-06-01 07:55:46 -04:00
}
2023-06-05 10:10:44 -04:00
return executeAllCmds ( ctx , a . s3Client , ops )
2023-06-01 07:55:46 -04:00
}
2023-06-25 17:32:39 -04:00
// List returns the list of versions for the given attestation variant.
2023-06-02 03:19:23 -04:00
func ( a Client ) List ( ctx context . Context , attestation variant . Variant ) ( [ ] string , error ) {
2023-06-05 06:33:22 -04:00
if attestation . Equal ( variant . AzureSEVSNP { } ) {
2023-06-07 10:16:32 -04:00
versions , err := apiclient . Fetch ( ctx , a . s3Client , AzureSEVSNPVersionList { } )
2023-06-05 06:33:22 -04:00
if err != nil {
return nil , err
}
return versions , nil
2023-05-25 12:43:44 -04:00
}
2023-06-25 17:32:39 -04:00
return nil , fmt . Errorf ( "unsupported attestation variant: %s" , attestation )
2023-05-25 12:43:44 -04:00
}
2023-06-07 10:16:32 -04:00
func ( a Client ) deleteAzureSEVSNPVersion ( versions AzureSEVSNPVersionList , versionStr string ) ( ops [ ] crudCmd , err error ) {
2023-06-05 06:33:22 -04:00
versionStr = versionStr + ".json"
ops = append ( ops , deleteCmd {
2023-06-07 10:16:32 -04:00
apiObject : AzureSEVSNPVersionAPI {
2023-06-05 06:33:22 -04:00
Version : versionStr ,
} ,
} )
ops = append ( ops , deleteCmd {
2023-06-07 10:16:32 -04:00
apiObject : AzureSEVSNPVersionSignature {
2023-06-05 06:33:22 -04:00
Version : versionStr ,
} ,
} )
removedVersions , err := removeVersion ( versions , versionStr )
if err != nil {
return nil , err
}
ops = append ( ops , putCmd {
apiObject : removedVersions ,
} )
return ops , nil
}
2023-06-07 10:16:32 -04:00
func ( a Client ) uploadAzureSEVSNP ( versions AzureSEVSNPVersion , versionNames [ ] string , date time . Time ) ( res [ ] crudCmd , err error ) {
2023-06-12 10:04:54 -04:00
dateStr := date . Format ( VersionFormat ) + ".json"
2023-06-05 10:10:44 -04:00
2023-06-07 10:16:32 -04:00
res = append ( res , putCmd { AzureSEVSNPVersionAPI { Version : dateStr , AzureSEVSNPVersion : versions } } )
2023-06-05 10:10:44 -04:00
versionBytes , err := json . Marshal ( versions )
2023-06-05 06:33:22 -04:00
if err != nil {
2023-06-05 10:10:44 -04:00
return res , err
2023-06-05 06:33:22 -04:00
}
2023-06-05 10:10:44 -04:00
signature , err := a . createSignature ( versionBytes , dateStr )
2023-05-25 12:43:44 -04:00
if err != nil {
2023-06-05 10:10:44 -04:00
return res , err
2023-06-05 06:33:22 -04:00
}
2023-06-05 10:10:44 -04:00
res = append ( res , putCmd { signature } )
newVersions := addVersion ( versionNames , dateStr )
2023-06-07 10:16:32 -04:00
res = append ( res , putCmd { AzureSEVSNPVersionList ( newVersions ) } )
2023-06-05 10:10:44 -04:00
return
2023-05-25 12:43:44 -04:00
}
2023-06-07 10:16:32 -04:00
func ( a Client ) createSignature ( content [ ] byte , dateStr string ) ( res AzureSEVSNPVersionSignature , err error ) {
2023-06-05 10:10:44 -04:00
signature , err := a . signer . Sign ( content )
2023-06-05 06:33:22 -04:00
if err != nil {
2023-06-05 10:10:44 -04:00
return res , fmt . Errorf ( "sign version file: %w" , err )
2023-05-25 12:43:44 -04:00
}
2023-06-07 10:16:32 -04:00
return AzureSEVSNPVersionSignature {
2023-06-05 10:10:44 -04:00
Signature : signature ,
Version : dateStr ,
} , nil
2023-06-01 07:55:46 -04:00
}
2023-06-07 10:16:32 -04:00
func removeVersion ( versions AzureSEVSNPVersionList , versionStr string ) ( removedVersions AzureSEVSNPVersionList , err error ) {
2023-06-05 06:33:22 -04:00
for i , v := range versions {
if v == versionStr {
if i == len ( versions ) - 1 {
removedVersions = versions [ : i ]
} else {
removedVersions = append ( versions [ : i ] , versions [ i + 1 : ] ... )
}
return removedVersions , nil
}
2023-06-01 07:55:46 -04:00
}
2023-06-05 06:33:22 -04:00
return nil , fmt . Errorf ( "version %s not found in list %v" , versionStr , versions )
2023-06-01 07:55:46 -04:00
}
2023-06-05 10:10:44 -04:00
type crudCmd interface {
Execute ( ctx context . Context , c * apiclient . Client ) error
}
2023-06-05 06:33:22 -04:00
type deleteCmd struct {
apiObject apiclient . APIObject
}
func ( d deleteCmd ) Execute ( ctx context . Context , c * apiclient . Client ) error {
return apiclient . Delete ( ctx , c , d . apiObject )
2023-05-25 12:43:44 -04:00
}
2023-06-02 05:20:01 -04:00
2023-06-05 06:33:22 -04:00
type putCmd struct {
apiObject apiclient . APIObject
2023-06-02 05:20:01 -04:00
}
2023-06-05 06:33:22 -04:00
func ( p putCmd ) Execute ( ctx context . Context , c * apiclient . Client ) error {
return apiclient . Update ( ctx , c , p . apiObject )
}
2023-06-05 10:10:44 -04:00
func executeAllCmds ( ctx context . Context , client * apiclient . Client , cmds [ ] crudCmd ) error {
for _ , cmd := range cmds {
if err := cmd . Execute ( ctx , client ) ; err != nil {
return fmt . Errorf ( "execute operation %+v: %w" , cmd , err )
}
}
return nil
2023-06-05 06:33:22 -04:00
}
func addVersion ( versions [ ] string , newVersion string ) [ ] string {
versions = append ( versions , newVersion )
versions = variant . RemoveDuplicate ( versions )
2023-06-09 06:48:12 -04:00
SortAzureSEVSNPVersionList ( versions )
2023-06-05 06:33:22 -04:00
return versions
}