From a06bf79527722442a42f6eee6691e6f74f96b25e Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 20 Oct 2021 10:59:30 +0200 Subject: [PATCH 01/36] First implementation --- bindings-go/apis/v2/cdutils/normalize.go | 232 +++++++++++++++++++++ bindings-go/apis/v2/componentdescriptor.go | 38 ++++ bindings-go/apis/v2/signatures/rsa.go | 86 ++++++++ bindings-go/apis/v2/signatures/types.go | 11 + bindings-go/main/main.go | 126 +++++++++++ 5 files changed, 493 insertions(+) create mode 100644 bindings-go/apis/v2/cdutils/normalize.go create mode 100644 bindings-go/apis/v2/signatures/rsa.go create mode 100644 bindings-go/apis/v2/signatures/types.go create mode 100644 bindings-go/main/main.go diff --git a/bindings-go/apis/v2/cdutils/normalize.go b/bindings-go/apis/v2/cdutils/normalize.go new file mode 100644 index 00000000..98b10445 --- /dev/null +++ b/bindings-go/apis/v2/cdutils/normalize.go @@ -0,0 +1,232 @@ +package cdutils + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "sort" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" +) + +type Entry map[string]interface{} + +func AddDigestsToComponentDescriptor(cd *v2.ComponentDescriptor, compRefResolver func(v2.ComponentDescriptor, v2.ComponentReference) v2.DigestSpec, + resResolver func(v2.ComponentDescriptor, v2.Resource) v2.DigestSpec) { + + for i, reference := range cd.ComponentReferences { + if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + cd.ComponentReferences[i].Digest = compRefResolver(*cd, reference) + } + } + + for i, res := range cd.Resources { + if res.Digest.Algorithm == "" || res.Digest.Value == "" { + cd.Resources[i].Digest = resResolver(*cd, res) + } + } +} + +// HashForComponentDescriptor return the hash for the component-descriptor, if it is normaliseable +// (= componentReferences and resources contain digest field) +func HashForComponentDescriptor(cd v2.ComponentDescriptor) (string, error) { + normalisedComponentDescriptor, err := normalizeComponentDescriptor(cd) + if err != nil { + return "", fmt.Errorf("failed normalising component descriptor %w", err) + } + hash := sha256.Sum256(normalisedComponentDescriptor) + + return hex.EncodeToString(hash[:]), nil +} + +func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer signatures.Signer) error { + hashCd, err := HashForComponentDescriptor(*cd) + if err != nil { + return fmt.Errorf("failed getting hash for cd: %w", err) + } + decodedHash, err := hex.DecodeString(hashCd) + if err != nil { + return fmt.Errorf("failed decoding hash to bytes") + } + + signature, err := signer.Sign(decodedHash) + if err != nil { + return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) + } + cd.Signatures = append(cd.Signatures, v2.Signature{NormalisationType: v2.NormalisationTypeV1, Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: hashCd, + }, + Signature: *signature, + }) + return nil +} + +// VerifySignedComponentDescriptor verifies the signature and hash of the component-descriptor. +// Returns error if verification fails. +func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier signatures.Verifier) error { + //Verify hash with signature + err := verifier.Verify(cd.Signatures[0]) + if err != nil { + return fmt.Errorf("failed verifying: %w", err) + } + + //Verify normalised cd to given (and verified) hash + hashCd, err := HashForComponentDescriptor(*cd) + if err != nil { + return fmt.Errorf("failed getting hash for cd: %w", err) + } + if hashCd != cd.Signatures[0].Digest.Value { + return fmt.Errorf("normalised component-descriptor does not match signed hash") + } + + return nil +} + +func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { + if err := isNormaliseable(cd); err != nil { + return nil, fmt.Errorf("can not normalise component-descriptor %s:%s: %w", cd.Name, cd.Version, err) + } + + var normalizedComponentDescriptor []Entry + + meta := []Entry{ + {"schemaVersion": cd.Metadata.Version}, + } + normalizedComponentDescriptor = append(normalizedComponentDescriptor, Entry{"meta": meta}) + + componentReferences := [][]Entry{} + for _, ref := range cd.ComponentSpec.ComponentReferences { + extraIdentity := buildExtraIdentity(ref.ExtraIdentity) + + digest := []Entry{ + {"algorithm": ref.Digest.Algorithm}, + {"value": ref.Digest.Value}, + } + + componentReference := []Entry{ + {"name": ref.Name}, + {"version": ref.Version}, + {"extraIdentity": extraIdentity}, + {"digest": digest}, + } + componentReferences = append(componentReferences, componentReference) + } + + resources := [][]Entry{} + for _, res := range cd.ComponentSpec.Resources { + extraIdentity := buildExtraIdentity(res.ExtraIdentity) + + digest := []Entry{ + {"algorithm": res.Digest.Algorithm}, + {"value": res.Digest.Value}, + } + + resource := []Entry{ + {"name": res.Name}, + {"version": res.Version}, + {"extraIdentity": extraIdentity}, + {"digest": digest}, + } + resources = append(resources, resource) + } + + sources := [][]Entry{} + for _, src := range cd.ComponentSpec.Sources { + extraIdentity := buildExtraIdentity(src.ExtraIdentity) + + source := []Entry{ + {"name": src.Name}, + {"version": src.Version}, + {"extraIdentity": extraIdentity}, + } + sources = append(sources, source) + } + + var componentSpec []Entry + componentSpec = append(componentSpec, Entry{"name": cd.ComponentSpec.Name}) + componentSpec = append(componentSpec, Entry{"version": cd.ComponentSpec.Version}) + componentSpec = append(componentSpec, Entry{"componentReferences": componentReferences}) + componentSpec = append(componentSpec, Entry{"resources": resources}) + componentSpec = append(componentSpec, Entry{"sources": sources}) + + normalizedComponentDescriptor = append(normalizedComponentDescriptor, Entry{"component": componentSpec}) + deepSort(normalizedComponentDescriptor) + normalizedString, err := json.Marshal(normalizedComponentDescriptor) + if err != nil { + return nil, err + } + + return normalizedString, nil +} + +func buildExtraIdentity(identity v2.Identity) []Entry { + var extraIdentities []Entry + for k, v := range identity { + extraIdentities = append(extraIdentities, Entry{k: v}) + } + return extraIdentities +} + +// deepSort sorts Entry, []Enry and [][]Entry interfaces recursively, lexicographicly by key(Entry). +func deepSort(in interface{}) { + switch castIn := in.(type) { + case []Entry: + for _, entry := range castIn { + var val interface{} + for _, v := range entry { + val = v + } + deepSort(val) + + } + sort.SliceStable(castIn, func(i, j int) bool { + var keyI string + for k := range castIn[i] { + keyI = k + } + + var keyJ string + for k := range castIn[j] { + keyJ = k + } + + return keyI < keyJ + }) + case Entry: + var val interface{} + for _, v := range castIn { + val = v + } + deepSort(val) + case [][]Entry: + for _, v := range castIn { + deepSort(v) + } + case string: + break + default: + fmt.Println("unknow type") + } +} + +// isNormaliseable checks if componentReferences and resources contain digest. +// Does NOT verify the digest is correct +func isNormaliseable(cd v2.ComponentDescriptor) error { + // check for digests on component references + for _, reference := range cd.ComponentReferences { + if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + return fmt.Errorf("missing digest in componentReference for %s:%s", reference.Name, reference.Version) + } + } + + // check for digests on resources + for _, res := range cd.Resources { + if res.Digest.Algorithm == "" || res.Digest.Value == "" { + return fmt.Errorf("missing digest in resource for %s:%s", res.Name, res.Version) + } + } + return nil +} diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index c388a7c2..68027a80 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -71,6 +71,9 @@ type ComponentDescriptor struct { Metadata Metadata `json:"meta"` // Spec contains the specification of the component. ComponentSpec `json:"component"` + + // Signatures contains a list of signatures for the ComponentDescriptor + Signatures []Signature `json:"signatures"` } // ComponentSpec defines a virtual component with @@ -351,6 +354,10 @@ type SourceRef struct { type Resource struct { IdentityObjectMeta `json:",inline"` + // Digest is the optional digest of the referenced component. + // +optional + Digest DigestSpec `json:"digest,omitempty"` + // Relation describes the relation of the resource to the component. // Can be a local or external resource Relation ResourceRelation `json:"relation,omitempty"` @@ -377,6 +384,9 @@ type ComponentReference struct { // ExtraIdentity is the identity of an object. // An additional label with key "name" ist not allowed ExtraIdentity Identity `json:"extraIdentity,omitempty"` + // Digest is the optional digest of the referenced component. + // +optional + Digest DigestSpec `json:"digest,omitempty"` // Labels defines an optional set of additional labels // describing the object. // +optional @@ -427,3 +437,31 @@ func (o *ComponentReference) GetIdentity() Identity { func (o *ComponentReference) GetIdentityDigest() []byte { return o.GetIdentity().Digest() } + +// DigestSpec defines the digest and algorithm. +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type DigestSpec struct { + Algorithm string `json:"algorithm"` + Value string `json:"value"` +} + +// SignatureSpec defines the signature and algorithm. +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type SignatureSpec struct { + Algorithm string `json:"algorithm"` + Data string `json:"data"` +} + +type NormalisationType string + +const ( + NormalisationTypeV1 NormalisationType = "v1" +) + +type Signature struct { + NormalisationType NormalisationType `json:"normalisationType"` + Digest DigestSpec `json:"digest"` + Signature SignatureSpec `json:"signature"` +} diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go new file mode 100644 index 00000000..ac7d4a12 --- /dev/null +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -0,0 +1,86 @@ +package signatures + +import ( + "crypto" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "io/ioutil" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" +) + +type RsaSigner struct { + privateKey rsa.PrivateKey +} + +func CreateRsaSignerFromKeyFile(pathToPrivateKey string) (*RsaSigner, error) { + privKeyFile, err := ioutil.ReadFile(pathToPrivateKey) + if err != nil { + return nil, fmt.Errorf("failed opening private key file %w", err) + } + + block, _ := pem.Decode([]byte(privKeyFile)) + if block == nil { + return nil, fmt.Errorf("failed decoding PEM formatted block in key %w", err) + } + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed parsing key %w", err) + } + return &RsaSigner{ + privateKey: *key, + }, nil +} + +func (s RsaSigner) Sign(data []byte) (*v2.SignatureSpec, error) { + signature, err := rsa.SignPKCS1v15(nil, &s.privateKey, crypto.SHA256, data) + if err != nil { + return nil, fmt.Errorf("failed signing hash, %w", err) + } + return &v2.SignatureSpec{ + Algorithm: "RSASSA-PKCS1-V1_5-SIGN", //TODO: check + Data: hex.EncodeToString(signature), + }, nil +} + +type RsaVerifier struct { + publicKey rsa.PublicKey +} + +func CreateRsaVerifierFromKeyFile(pathToPublicKey string) (*RsaVerifier, error) { + publicKey, err := ioutil.ReadFile(pathToPublicKey) + if err != nil { + return nil, fmt.Errorf("failed opening public key file %w", err) + } + block, _ := pem.Decode([]byte(publicKey)) + if block == nil { + return nil, fmt.Errorf("failed decoding PEM formatted block in key %w", err) + } + untypedKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed parsing key %w", err) + } + key := untypedKey.(*rsa.PublicKey) + return &RsaVerifier{ + publicKey: *key, + }, nil +} + +func (v RsaVerifier) Verify(signature v2.Signature) error { + decodedHash, err := hex.DecodeString(signature.Digest.Value) + if err != nil { + return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) + } + decodedSignature, err := hex.DecodeString(signature.Signature.Data) + if err != nil { + return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) + } + err = rsa.VerifyPKCS1v15(&v.publicKey, crypto.SHA256, decodedHash, decodedSignature) + if err != nil { + return fmt.Errorf("signature verification failed, %w", err) + } + return nil +} diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go new file mode 100644 index 00000000..bb9c37d8 --- /dev/null +++ b/bindings-go/apis/v2/signatures/types.go @@ -0,0 +1,11 @@ +package signatures + +import v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + +type Signer interface { + Sign(data []byte) (*v2.SignatureSpec, error) +} + +type Verifier interface { + Verify(signature v2.Signature) error +} diff --git a/bindings-go/main/main.go b/bindings-go/main/main.go new file mode 100644 index 00000000..bce2e17d --- /dev/null +++ b/bindings-go/main/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "fmt" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/cdutils" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" +) + +func main() { + cd := v2.ComponentDescriptor{ + Metadata: v2.Metadata{ + Version: "v2", + }, + ComponentSpec: v2.ComponentSpec{ + ObjectMeta: v2.ObjectMeta{ + Name: "Name", + Version: "Version", + }, + ComponentReferences: []v2.ComponentReference{ + v2.ComponentReference{ + Name: "refName", + ComponentName: "unimportantName", + Version: "v1ref", + ExtraIdentity: v2.Identity{ + "refKey": "refName", + }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "iuhadshksasad", + }, + }, + }, + Resources: []v2.Resource{ + v2.Resource{ + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "Resource1", + Version: "v1", + ExtraIdentity: v2.Identity{ + "key": "value", + }, + }, + }, + v2.Resource{ + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "Resource2", + Version: "v2", + ExtraIdentity: v2.Identity{ + "zzz": "valuezzz", + "key": "value", + }, + }, + }, + }, + Sources: []v2.Source{ + v2.Source{ + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "source1", + Version: "v0.1", + ExtraIdentity: v2.Identity{ + "key2": "value2", + "key1": "value1", + }, + }, + }, + }, + }, + Signatures: []v2.Signature{ + v2.Signature{NormalisationType: v2.NormalisationTypeV1, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "4827a3a57c4eefd68c0921b2946de8017a082cd51c1ebc0957297f2fb794557e", + }, + Signature: v2.SignatureSpec{ + Algorithm: "RSASSA-PKCS1-V1_5-SIGN", + Data: "3d7a81955e1b7cb9556fedb0886229f8b65f1b7ab2cc7be7c1dbe4acff79e2de7415eb1af26402b9d0dd8cdcb90fab212cf122c223bb502900674c90c251b91c044a864b057d1ec4672710ffa79198ab170746100fc2da3b7d78dbfd7a8260bd4adaaf6fccba0d01d7f0a371bdefce9a9792bb1ff4f3e48ad64e33d6a17609a3895d203e17b813ed4f4d4b5f1ef2a803bc2237e8021a92a3bc8780d2617553ece7dfb406af2e3f44000596968476d65b1a4f5e533692c0779823d56fff18c91a9a240a0efd76d012f17f3ecb37152f962590e049274652c634328f70d72ed78e602da182c274f6ded518be560f6846b57cf07227c90b19207b625466a31c0524dcb548433d626200e934a8ae50f987bfe4906fd1db2f1f0337f9ca18793625aedfa4eab2f4fc3b24fdf2c1a6cb2c2f8c4c7082c1b0f037c72d550c0f523349fc6061821d314586939507084ea441437b87152b58b521f1251f4be411e9e96236a385d76968316b4c2eace125417a43730c1bef02db8a0eb8a404352bd8390eda6b4af94681810122917dd59cf11249eb32c8d464f6f34cd7dbd5207efca4275bd20beb06f1afbb112b35980510f50bb4fa958a2426faad74511c48cf1d71abfb6b2bf7291bbb69fcb080a8461b3cd335ca259bd2e54e879d6884c3bf0473922171c018cfb8926ca2d9a0f79fb4618658f22fce7a56d55affd913618124a79e09", + }, + }, + }, + } + + cdutils.AddDigestsToComponentDescriptor(&cd, func(cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { + return v2.DigestSpec{ + Algorithm: "testing", + Value: string(cr.GetIdentityDigest()), + } + }, func(cd v2.ComponentDescriptor, r v2.Resource) v2.DigestSpec { + return v2.DigestSpec{ + Algorithm: "testing", + Value: string(r.GetIdentityDigest()), + } + }) + + norm, err := cdutils.HashForComponentDescriptor(cd) + if err != nil { + fmt.Printf("ERROR: %s", err) + return + } + fmt.Println(norm) + + signer, err := signatures.CreateRsaSignerFromKeyFile("private") + if err != nil { + fmt.Printf("ERROR create signer: %s", err) + return + } + + err = cdutils.SignComponentDescriptor(&cd, signer) + if err != nil { + fmt.Printf("ERROR sign: %s", err) + return + } + fmt.Println(cd) + + verifier, err := signatures.CreateRsaVerifierFromKeyFile("public") + if err != nil { + fmt.Printf("ERROR create verifier: %s", err) + return + } + err = cdutils.VerifySignedComponentDescriptor(&cd, verifier) + if err != nil { + fmt.Printf("ERROR verify signature: %s", err) + return + } + fmt.Println("If not error is printed, successful") +} From ac968635e62133385cc2c7c591acaaa67c3537dd Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 22 Oct 2021 13:36:20 +0200 Subject: [PATCH 02/36] Feedbacj improvements --- bindings-go/apis/v2/componentdescriptor.go | 4 ++ .../v2/{cdutils => signatures}/normalize.go | 66 ++----------------- bindings-go/apis/v2/signatures/rsa.go | 4 +- bindings-go/apis/v2/signatures/sign.go | 54 +++++++++++++++ bindings-go/apis/v2/signatures/types.go | 6 +- .../{main => examples/signatures}/main.go | 9 ++- 6 files changed, 73 insertions(+), 70 deletions(-) rename bindings-go/apis/v2/{cdutils => signatures}/normalize.go (71%) create mode 100644 bindings-go/apis/v2/signatures/sign.go rename bindings-go/{main => examples/signatures}/main.go (91%) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 68027a80..6639ca8b 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -460,7 +460,11 @@ const ( NormalisationTypeV1 NormalisationType = "v1" ) +// Signature defines a digest and corresponding signature, identifyable by name. +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true type Signature struct { + Name string `json:"name"` NormalisationType NormalisationType `json:"normalisationType"` Digest DigestSpec `json:"digest"` Signature SignatureSpec `json:"signature"` diff --git a/bindings-go/apis/v2/cdutils/normalize.go b/bindings-go/apis/v2/signatures/normalize.go similarity index 71% rename from bindings-go/apis/v2/cdutils/normalize.go rename to bindings-go/apis/v2/signatures/normalize.go index 98b10445..75500b29 100644 --- a/bindings-go/apis/v2/cdutils/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -1,4 +1,4 @@ -package cdutils +package signatures import ( "crypto/sha256" @@ -8,11 +8,12 @@ import ( "sort" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" - "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" ) +// Entry is used for normalisation and has to contain one key type Entry map[string]interface{} +// AddDigestsToComponentDescriptor adds digest to componentReferences and resources as returned in the resolver functions func AddDigestsToComponentDescriptor(cd *v2.ComponentDescriptor, compRefResolver func(v2.ComponentDescriptor, v2.ComponentReference) v2.DigestSpec, resResolver func(v2.ComponentDescriptor, v2.Resource) v2.DigestSpec) { @@ -41,50 +42,6 @@ func HashForComponentDescriptor(cd v2.ComponentDescriptor) (string, error) { return hex.EncodeToString(hash[:]), nil } -func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer signatures.Signer) error { - hashCd, err := HashForComponentDescriptor(*cd) - if err != nil { - return fmt.Errorf("failed getting hash for cd: %w", err) - } - decodedHash, err := hex.DecodeString(hashCd) - if err != nil { - return fmt.Errorf("failed decoding hash to bytes") - } - - signature, err := signer.Sign(decodedHash) - if err != nil { - return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) - } - cd.Signatures = append(cd.Signatures, v2.Signature{NormalisationType: v2.NormalisationTypeV1, Digest: v2.DigestSpec{ - Algorithm: "sha256", - Value: hashCd, - }, - Signature: *signature, - }) - return nil -} - -// VerifySignedComponentDescriptor verifies the signature and hash of the component-descriptor. -// Returns error if verification fails. -func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier signatures.Verifier) error { - //Verify hash with signature - err := verifier.Verify(cd.Signatures[0]) - if err != nil { - return fmt.Errorf("failed verifying: %w", err) - } - - //Verify normalised cd to given (and verified) hash - hashCd, err := HashForComponentDescriptor(*cd) - if err != nil { - return fmt.Errorf("failed getting hash for cd: %w", err) - } - if hashCd != cd.Signatures[0].Digest.Value { - return fmt.Errorf("normalised component-descriptor does not match signed hash") - } - - return nil -} - func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { if err := isNormaliseable(cd); err != nil { return nil, fmt.Errorf("can not normalise component-descriptor %s:%s: %w", cd.Name, cd.Version, err) @@ -133,24 +90,11 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { resources = append(resources, resource) } - sources := [][]Entry{} - for _, src := range cd.ComponentSpec.Sources { - extraIdentity := buildExtraIdentity(src.ExtraIdentity) - - source := []Entry{ - {"name": src.Name}, - {"version": src.Version}, - {"extraIdentity": extraIdentity}, - } - sources = append(sources, source) - } - var componentSpec []Entry componentSpec = append(componentSpec, Entry{"name": cd.ComponentSpec.Name}) componentSpec = append(componentSpec, Entry{"version": cd.ComponentSpec.Version}) componentSpec = append(componentSpec, Entry{"componentReferences": componentReferences}) componentSpec = append(componentSpec, Entry{"resources": resources}) - componentSpec = append(componentSpec, Entry{"sources": sources}) normalizedComponentDescriptor = append(normalizedComponentDescriptor, Entry{"component": componentSpec}) deepSort(normalizedComponentDescriptor) @@ -212,8 +156,8 @@ func deepSort(in interface{}) { } } -// isNormaliseable checks if componentReferences and resources contain digest. -// Does NOT verify the digest is correct +// isNormaliseable checks if componentReferences and resources contain digest +// Does NOT verify if the digest are correct func isNormaliseable(cd v2.ComponentDescriptor) error { // check for digests on component references for _, reference := range cd.ComponentReferences { diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index ac7d4a12..c1967c15 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -35,7 +35,7 @@ func CreateRsaSignerFromKeyFile(pathToPrivateKey string) (*RsaSigner, error) { }, nil } -func (s RsaSigner) Sign(data []byte) (*v2.SignatureSpec, error) { +func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, data []byte) (*v2.SignatureSpec, error) { signature, err := rsa.SignPKCS1v15(nil, &s.privateKey, crypto.SHA256, data) if err != nil { return nil, fmt.Errorf("failed signing hash, %w", err) @@ -69,7 +69,7 @@ func CreateRsaVerifierFromKeyFile(pathToPublicKey string) (*RsaVerifier, error) }, nil } -func (v RsaVerifier) Verify(signature v2.Signature) error { +func (v RsaVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { decodedHash, err := hex.DecodeString(signature.Digest.Value) if err != nil { return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) diff --git a/bindings-go/apis/v2/signatures/sign.go b/bindings-go/apis/v2/signatures/sign.go new file mode 100644 index 00000000..593b5bf6 --- /dev/null +++ b/bindings-go/apis/v2/signatures/sign.go @@ -0,0 +1,54 @@ +package signatures + +import ( + "encoding/hex" + "fmt" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" +) + +// SignComponentDescriptor signs the given component-descriptor with the signer. +// The component-descriptor has to contain digests for componentReferences and resources. +func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer) error { + hashCd, err := HashForComponentDescriptor(*cd) + if err != nil { + return fmt.Errorf("failed getting hash for cd: %w", err) + } + decodedHash, err := hex.DecodeString(hashCd) + if err != nil { + return fmt.Errorf("failed decoding hash to bytes") + } + + signature, err := signer.Sign(*cd, decodedHash) + if err != nil { + return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) + } + cd.Signatures = append(cd.Signatures, v2.Signature{NormalisationType: v2.NormalisationTypeV1, Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: hashCd, + }, + Signature: *signature, + }) + return nil +} + +// VerifySignedComponentDescriptor verifies the signature and hash of the component-descriptor. +// Returns error if verification fails. +func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifier) error { + //Verify hash with signature + err := verifier.Verify(*cd, cd.Signatures[0]) //TODO: select by name + if err != nil { + return fmt.Errorf("failed verifying: %w", err) + } + + //Verify normalised cd to given (and verified) hash + hashCd, err := HashForComponentDescriptor(*cd) + if err != nil { + return fmt.Errorf("failed getting hash for cd: %w", err) + } + if hashCd != cd.Signatures[0].Digest.Value { + return fmt.Errorf("normalised component-descriptor does not match signed hash") + } + + return nil +} diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index bb9c37d8..9638a281 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -3,9 +3,11 @@ package signatures import v2 "github.com/gardener/component-spec/bindings-go/apis/v2" type Signer interface { - Sign(data []byte) (*v2.SignatureSpec, error) + // Sign returns the signature for the data for the component-descriptor + Sign(componentDescriptor v2.ComponentDescriptor, data []byte) (*v2.SignatureSpec, error) } type Verifier interface { - Verify(signature v2.Signature) error + // Verify checks the signature, returns an error on verification failure + Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error } diff --git a/bindings-go/main/main.go b/bindings-go/examples/signatures/main.go similarity index 91% rename from bindings-go/main/main.go rename to bindings-go/examples/signatures/main.go index bce2e17d..c98c8cb1 100644 --- a/bindings-go/main/main.go +++ b/bindings-go/examples/signatures/main.go @@ -4,7 +4,6 @@ import ( "fmt" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" - "github.com/gardener/component-spec/bindings-go/apis/v2/cdutils" "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" ) @@ -80,7 +79,7 @@ func main() { }, } - cdutils.AddDigestsToComponentDescriptor(&cd, func(cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { + signatures.AddDigestsToComponentDescriptor(&cd, func(cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { return v2.DigestSpec{ Algorithm: "testing", Value: string(cr.GetIdentityDigest()), @@ -92,7 +91,7 @@ func main() { } }) - norm, err := cdutils.HashForComponentDescriptor(cd) + norm, err := signatures.HashForComponentDescriptor(cd) if err != nil { fmt.Printf("ERROR: %s", err) return @@ -105,7 +104,7 @@ func main() { return } - err = cdutils.SignComponentDescriptor(&cd, signer) + err = signatures.SignComponentDescriptor(&cd, signer) if err != nil { fmt.Printf("ERROR sign: %s", err) return @@ -117,7 +116,7 @@ func main() { fmt.Printf("ERROR create verifier: %s", err) return } - err = cdutils.VerifySignedComponentDescriptor(&cd, verifier) + err = signatures.VerifySignedComponentDescriptor(&cd, verifier) if err != nil { fmt.Printf("ERROR verify signature: %s", err) return From f9fc5f5bcf5bf0b0b932c8c60f90368da97f45da Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Mon, 25 Oct 2021 10:19:04 +0200 Subject: [PATCH 03/36] improvements syntax --- bindings-go/apis/v2/signatures/normalize.go | 67 +++++++++++---------- bindings-go/examples/signatures/main.go | 2 +- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 75500b29..fc1968e9 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -47,12 +47,9 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { return nil, fmt.Errorf("can not normalise component-descriptor %s:%s: %w", cd.Name, cd.Version, err) } - var normalizedComponentDescriptor []Entry - meta := []Entry{ {"schemaVersion": cd.Metadata.Version}, } - normalizedComponentDescriptor = append(normalizedComponentDescriptor, Entry{"meta": meta}) componentReferences := [][]Entry{} for _, ref := range cd.ComponentSpec.ComponentReferences { @@ -90,20 +87,27 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { resources = append(resources, resource) } - var componentSpec []Entry - componentSpec = append(componentSpec, Entry{"name": cd.ComponentSpec.Name}) - componentSpec = append(componentSpec, Entry{"version": cd.ComponentSpec.Version}) - componentSpec = append(componentSpec, Entry{"componentReferences": componentReferences}) - componentSpec = append(componentSpec, Entry{"resources": resources}) + componentSpec := []Entry{ + {"name": cd.ComponentSpec.Name}, + {"version": cd.ComponentSpec.Version}, + {"componentReferences": componentReferences}, + {"resources": resources}, + } + + normalizedComponentDescriptor := []Entry{ + {"meta": meta}, + {"component": componentSpec}, + } - normalizedComponentDescriptor = append(normalizedComponentDescriptor, Entry{"component": componentSpec}) deepSort(normalizedComponentDescriptor) - normalizedString, err := json.Marshal(normalizedComponentDescriptor) + + normalizedJson, err := json.Marshal(normalizedComponentDescriptor) + if err != nil { return nil, err } - return normalizedString, nil + return normalizedJson, nil } func buildExtraIdentity(identity v2.Identity) []Entry { @@ -118,32 +122,17 @@ func buildExtraIdentity(identity v2.Identity) []Entry { func deepSort(in interface{}) { switch castIn := in.(type) { case []Entry: + // sort the values recursively for every entry for _, entry := range castIn { - var val interface{} - for _, v := range entry { - val = v - } + val := getOnlyValueInEntry(entry) deepSort(val) - } + // sort the entries based on the key sort.SliceStable(castIn, func(i, j int) bool { - var keyI string - for k := range castIn[i] { - keyI = k - } - - var keyJ string - for k := range castIn[j] { - keyJ = k - } - - return keyI < keyJ + return getOnlyKeyInEntry(castIn[i]) < getOnlyKeyInEntry(castIn[j]) }) case Entry: - var val interface{} - for _, v := range castIn { - val = v - } + val := getOnlyValueInEntry(castIn) deepSort(val) case [][]Entry: for _, v := range castIn { @@ -156,6 +145,22 @@ func deepSort(in interface{}) { } } +func getOnlyKeyInEntry(entry Entry) string { + var key string + for k := range entry { + key = k + } + return key +} + +func getOnlyValueInEntry(entry Entry) interface{} { + var value interface{} + for _, v := range entry { + value = v + } + return value +} + // isNormaliseable checks if componentReferences and resources contain digest // Does NOT verify if the digest are correct func isNormaliseable(cd v2.ComponentDescriptor) error { diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index c98c8cb1..a00d6145 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -69,7 +69,7 @@ func main() { v2.Signature{NormalisationType: v2.NormalisationTypeV1, Digest: v2.DigestSpec{ Algorithm: "sha256", - Value: "4827a3a57c4eefd68c0921b2946de8017a082cd51c1ebc0957297f2fb794557e", + Value: "d782bbae5f6df38c1b7df79319ee6a9625dafcce5df3d730b25aee55db63fcfa", }, Signature: v2.SignatureSpec{ Algorithm: "RSASSA-PKCS1-V1_5-SIGN", From 946c8c340385997782ef53dec129bef432c7aa26 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 26 Oct 2021 09:24:45 +0200 Subject: [PATCH 04/36] hash function now exchangeable --- bindings-go/apis/v2/signatures/normalize.go | 17 ++++--- bindings-go/apis/v2/signatures/rsa.go | 34 +++++++++++-- bindings-go/apis/v2/signatures/sign.go | 56 +++++++++++++++------ bindings-go/apis/v2/signatures/types.go | 27 +++++++++- bindings-go/examples/signatures/main.go | 14 ++++-- 5 files changed, 116 insertions(+), 32 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index fc1968e9..e5ad8c20 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -1,10 +1,9 @@ package signatures import ( - "crypto/sha256" - "encoding/hex" "encoding/json" "fmt" + "hash" "sort" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" @@ -32,14 +31,18 @@ func AddDigestsToComponentDescriptor(cd *v2.ComponentDescriptor, compRefResolver // HashForComponentDescriptor return the hash for the component-descriptor, if it is normaliseable // (= componentReferences and resources contain digest field) -func HashForComponentDescriptor(cd v2.ComponentDescriptor) (string, error) { +func HashForComponentDescriptor(cd v2.ComponentDescriptor, hashFunction hash.Hash) ([]byte, error) { normalisedComponentDescriptor, err := normalizeComponentDescriptor(cd) if err != nil { - return "", fmt.Errorf("failed normalising component descriptor %w", err) + return nil, fmt.Errorf("failed normalising component descriptor %w", err) } - hash := sha256.Sum256(normalisedComponentDescriptor) - - return hex.EncodeToString(hash[:]), nil + hashFunction.Reset() + _, err = hashFunction.Write(normalisedComponentDescriptor) + if err != nil { + return nil, fmt.Errorf("failed hashing the normalisedComponentDescirptorJson: %w", err) + } + hash := hashFunction.Sum(nil) + return hash, nil } func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index c1967c15..29cd67df 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -8,6 +8,7 @@ import ( "encoding/pem" "fmt" "io/ioutil" + "strings" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" ) @@ -35,8 +36,16 @@ func CreateRsaSignerFromKeyFile(pathToPrivateKey string) (*RsaSigner, error) { }, nil } -func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, data []byte) (*v2.SignatureSpec, error) { - signature, err := rsa.SignPKCS1v15(nil, &s.privateKey, crypto.SHA256, data) +func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) { + decodedHash, err := hex.DecodeString(digest.Value) + if err != nil { + return nil, fmt.Errorf("failed decoding hash to bytes") + } + hashType, err := hashAlgorithmLookup(digest.Algorithm) + if err != nil { + return nil, fmt.Errorf("failed looking up hash algorithm") + } + signature, err := rsa.SignPKCS1v15(nil, &s.privateKey, hashType, decodedHash) if err != nil { return nil, fmt.Errorf("failed signing hash, %w", err) } @@ -46,6 +55,21 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, data []byte) }, nil } +// maps a hashing algorithm string to crypto.Hash +func hashAlgorithmLookup(algorithm string) (crypto.Hash, error) { + switch strings.ToUpper(algorithm) { + case "SHA256": + return crypto.SHA256, nil + case "SHA512": + return crypto.SHA512, nil //TODO: test + case "SHA3_256": + return crypto.SHA3_256, nil // TODO: test + case "SHA3_512": + return crypto.SHA3_512, nil // TODO: test + } + return 0, fmt.Errorf("hash Algorithm %s not found", algorithm) +} + type RsaVerifier struct { publicKey rsa.PublicKey } @@ -78,7 +102,11 @@ func (v RsaVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signatur if err != nil { return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) } - err = rsa.VerifyPKCS1v15(&v.publicKey, crypto.SHA256, decodedHash, decodedSignature) + algorithm, err := hashAlgorithmLookup(signature.Digest.Algorithm) + if err != nil { + return fmt.Errorf("failed looking up hash algorithm for %s: %w", signature.Digest.Algorithm, err) + } + err = rsa.VerifyPKCS1v15(&v.publicKey, algorithm, decodedHash, decodedSignature) if err != nil { return fmt.Errorf("signature verification failed, %w", err) } diff --git a/bindings-go/apis/v2/signatures/sign.go b/bindings-go/apis/v2/signatures/sign.go index 593b5bf6..1a34d408 100644 --- a/bindings-go/apis/v2/signatures/sign.go +++ b/bindings-go/apis/v2/signatures/sign.go @@ -9,46 +9,70 @@ import ( // SignComponentDescriptor signs the given component-descriptor with the signer. // The component-descriptor has to contain digests for componentReferences and resources. -func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer) error { - hashCd, err := HashForComponentDescriptor(*cd) +func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer, hasher Hasher, signatureName string) error { + hashCd, err := HashForComponentDescriptor(*cd, hasher.HashFunction) if err != nil { return fmt.Errorf("failed getting hash for cd: %w", err) } - decodedHash, err := hex.DecodeString(hashCd) - if err != nil { - return fmt.Errorf("failed decoding hash to bytes") + + digest := v2.DigestSpec{ + Algorithm: hasher.AlgorithmName, + Value: hex.EncodeToString(hashCd), } - signature, err := signer.Sign(*cd, decodedHash) + signature, err := signer.Sign(*cd, digest) if err != nil { return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) } - cd.Signatures = append(cd.Signatures, v2.Signature{NormalisationType: v2.NormalisationTypeV1, Digest: v2.DigestSpec{ - Algorithm: "sha256", - Value: hashCd, - }, - Signature: *signature, + cd.Signatures = append(cd.Signatures, v2.Signature{ + Name: signatureName, + NormalisationType: v2.NormalisationTypeV1, + Digest: digest, + Signature: *signature, }) return nil } -// VerifySignedComponentDescriptor verifies the signature and hash of the component-descriptor. +// VerifySignedComponentDescriptor verifies the signature (selected by signatureName) and hash of the component-descriptor (as specified in the signature). // Returns error if verification fails. -func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifier) error { +func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifier, signatureName string) error { + //find matching signature + + matchingSignature, err := selectSignatureByName(cd, signatureName) + if err != nil { + return fmt.Errorf("failed checking signature: %w", err) + } + //Verify hash with signature - err := verifier.Verify(*cd, cd.Signatures[0]) //TODO: select by name + err = verifier.Verify(*cd, *matchingSignature) if err != nil { return fmt.Errorf("failed verifying: %w", err) } + //get hasher by algorithm name + hasher, err := HasherForName(matchingSignature.Digest.Algorithm) + if err != nil { + return fmt.Errorf("failed creating hasher for %s: %w", matchingSignature.Digest.Algorithm, err) + } + //Verify normalised cd to given (and verified) hash - hashCd, err := HashForComponentDescriptor(*cd) + hashCd, err := HashForComponentDescriptor(*cd, hasher.HashFunction) if err != nil { return fmt.Errorf("failed getting hash for cd: %w", err) } - if hashCd != cd.Signatures[0].Digest.Value { + if hex.EncodeToString(hashCd) != matchingSignature.Digest.Value { return fmt.Errorf("normalised component-descriptor does not match signed hash") } return nil } + +func selectSignatureByName(cd *v2.ComponentDescriptor, signatureName string) (*v2.Signature, error) { + for _, signature := range cd.Signatures { + if signature.Name == signatureName { + return &signature, nil + } + } + return nil, fmt.Errorf("signature with name %s not found in component-descriptor", signatureName) + +} diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index 9638a281..34931683 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -1,13 +1,36 @@ package signatures -import v2 "github.com/gardener/component-spec/bindings-go/apis/v2" +import ( + "crypto/sha256" + "fmt" + "hash" + "strings" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" +) type Signer interface { // Sign returns the signature for the data for the component-descriptor - Sign(componentDescriptor v2.ComponentDescriptor, data []byte) (*v2.SignatureSpec, error) + Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) } type Verifier interface { // Verify checks the signature, returns an error on verification failure Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error } + +type Hasher struct { + HashFunction hash.Hash + AlgorithmName string +} + +func HasherForName(algorithmName string) (*Hasher, error) { + switch strings.ToUpper(algorithmName) { + case "SHA256": + return &Hasher{ + HashFunction: sha256.New(), + AlgorithmName: "SHA256", + }, nil + } + return nil, fmt.Errorf("hash algorithm %s not found/implemented", algorithmName) +} diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index a00d6145..d094497c 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/hex" "fmt" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" @@ -91,12 +92,17 @@ func main() { } }) - norm, err := signatures.HashForComponentDescriptor(cd) + hasher, err := signatures.HasherForName("SHA256") + if err != nil { + fmt.Printf("ERROR: %s", err) + } + + norm, err := signatures.HashForComponentDescriptor(cd, hasher.HashFunction) if err != nil { fmt.Printf("ERROR: %s", err) return } - fmt.Println(norm) + fmt.Println(hex.EncodeToString(norm)) signer, err := signatures.CreateRsaSignerFromKeyFile("private") if err != nil { @@ -104,7 +110,7 @@ func main() { return } - err = signatures.SignComponentDescriptor(&cd, signer) + err = signatures.SignComponentDescriptor(&cd, signer, *hasher, "mySignatureName") if err != nil { fmt.Printf("ERROR sign: %s", err) return @@ -116,7 +122,7 @@ func main() { fmt.Printf("ERROR create verifier: %s", err) return } - err = signatures.VerifySignedComponentDescriptor(&cd, verifier) + err = signatures.VerifySignedComponentDescriptor(&cd, verifier, "mySignatureName") if err != nil { fmt.Printf("ERROR verify signature: %s", err) return From 6c0da396b3cb2472ff9db7579f2b1ec89dda253b Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 26 Oct 2021 16:53:04 +0200 Subject: [PATCH 05/36] add context to digestToComponentDescriptor resolvers --- bindings-go/apis/v2/signatures/normalize.go | 10 ++++++---- bindings-go/examples/signatures/main.go | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index e5ad8c20..f448adcf 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -1,6 +1,7 @@ package signatures import ( + "context" "encoding/json" "fmt" "hash" @@ -13,18 +14,19 @@ import ( type Entry map[string]interface{} // AddDigestsToComponentDescriptor adds digest to componentReferences and resources as returned in the resolver functions -func AddDigestsToComponentDescriptor(cd *v2.ComponentDescriptor, compRefResolver func(v2.ComponentDescriptor, v2.ComponentReference) v2.DigestSpec, - resResolver func(v2.ComponentDescriptor, v2.Resource) v2.DigestSpec) { +func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescriptor, + compRefResolver func(context.Context, v2.ComponentDescriptor, v2.ComponentReference) v2.DigestSpec, + resResolver func(context.Context, v2.ComponentDescriptor, v2.Resource) v2.DigestSpec) { for i, reference := range cd.ComponentReferences { if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { - cd.ComponentReferences[i].Digest = compRefResolver(*cd, reference) + cd.ComponentReferences[i].Digest = compRefResolver(ctx, *cd, reference) } } for i, res := range cd.Resources { if res.Digest.Algorithm == "" || res.Digest.Value == "" { - cd.Resources[i].Digest = resResolver(*cd, res) + cd.Resources[i].Digest = resResolver(ctx, *cd, res) } } } diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index d094497c..f460af5c 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/hex" "fmt" @@ -79,13 +80,13 @@ func main() { }, }, } - - signatures.AddDigestsToComponentDescriptor(&cd, func(cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { + ctx := context.TODO() + signatures.AddDigestsToComponentDescriptor(ctx, &cd, func(ctx context.Context, cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { return v2.DigestSpec{ Algorithm: "testing", Value: string(cr.GetIdentityDigest()), } - }, func(cd v2.ComponentDescriptor, r v2.Resource) v2.DigestSpec { + }, func(ctx context.Context, cd v2.ComponentDescriptor, r v2.Resource) v2.DigestSpec { return v2.DigestSpec{ Algorithm: "testing", Value: string(r.GetIdentityDigest()), From bc2cc68af9c9c242c59e4537e6b3096aaf05bcad Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 26 Oct 2021 17:24:00 +0200 Subject: [PATCH 06/36] add error to componentReference and resourceResolver callbacks --- bindings-go/apis/v2/signatures/normalize.go | 17 +++++++++++++---- bindings-go/examples/signatures/main.go | 15 +++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index f448adcf..ec36a025 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -15,20 +15,29 @@ type Entry map[string]interface{} // AddDigestsToComponentDescriptor adds digest to componentReferences and resources as returned in the resolver functions func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescriptor, - compRefResolver func(context.Context, v2.ComponentDescriptor, v2.ComponentReference) v2.DigestSpec, - resResolver func(context.Context, v2.ComponentDescriptor, v2.Resource) v2.DigestSpec) { + compRefResolver func(context.Context, v2.ComponentDescriptor, v2.ComponentReference) (*v2.DigestSpec, error), + resResolver func(context.Context, v2.ComponentDescriptor, v2.Resource) (*v2.DigestSpec, error)) error { for i, reference := range cd.ComponentReferences { if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { - cd.ComponentReferences[i].Digest = compRefResolver(ctx, *cd, reference) + digest, err := compRefResolver(ctx, *cd, reference) + if err != nil { + return fmt.Errorf("failed resolving componentReference for %s:%s: %w", reference.Name, reference.Version, err) + } + cd.ComponentReferences[i].Digest = *digest } } for i, res := range cd.Resources { if res.Digest.Algorithm == "" || res.Digest.Value == "" { - cd.Resources[i].Digest = resResolver(ctx, *cd, res) + digest, err := resResolver(ctx, *cd, res) + if err != nil { + return fmt.Errorf("failed resolving resource for %s:%s: %w", res.Name, res.Version, err) + } + cd.Resources[i].Digest = *digest } } + return nil } // HashForComponentDescriptor return the hash for the component-descriptor, if it is normaliseable diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index f460af5c..d31a04f9 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -81,17 +81,20 @@ func main() { }, } ctx := context.TODO() - signatures.AddDigestsToComponentDescriptor(ctx, &cd, func(ctx context.Context, cd v2.ComponentDescriptor, cr v2.ComponentReference) v2.DigestSpec { - return v2.DigestSpec{ + err := signatures.AddDigestsToComponentDescriptor(ctx, &cd, func(ctx context.Context, cd v2.ComponentDescriptor, cr v2.ComponentReference) (*v2.DigestSpec, error) { + return &v2.DigestSpec{ Algorithm: "testing", Value: string(cr.GetIdentityDigest()), - } - }, func(ctx context.Context, cd v2.ComponentDescriptor, r v2.Resource) v2.DigestSpec { - return v2.DigestSpec{ + }, nil + }, func(ctx context.Context, cd v2.ComponentDescriptor, r v2.Resource) (*v2.DigestSpec, error) { + return &v2.DigestSpec{ Algorithm: "testing", Value: string(r.GetIdentityDigest()), - } + }, nil }) + if err != nil { + fmt.Printf("ERROR addingDigestsToComponentDescriptor %s", err) + } hasher, err := signatures.HasherForName("SHA256") if err != nil { From bdc91cf3ec0b1767e7dfc56a5a0ab55b24711135 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 27 Oct 2021 10:40:27 +0200 Subject: [PATCH 07/36] normalisation type to normalisation version --- bindings-go/apis/v2/componentdescriptor.go | 12 ++++++------ bindings-go/apis/v2/signatures/sign.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 6639ca8b..4f281d42 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -454,18 +454,18 @@ type SignatureSpec struct { Data string `json:"data"` } -type NormalisationType string +type NormalisationVersion string const ( - NormalisationTypeV1 NormalisationType = "v1" + NormalisationVersionV1 NormalisationVersion = "v1" ) // Signature defines a digest and corresponding signature, identifyable by name. // +k8s:deepcopy-gen=true // +k8s:openapi-gen=true type Signature struct { - Name string `json:"name"` - NormalisationType NormalisationType `json:"normalisationType"` - Digest DigestSpec `json:"digest"` - Signature SignatureSpec `json:"signature"` + Name string `json:"name"` + NormalisationVersion NormalisationVersion `json:"normalisationVersion"` + Digest DigestSpec `json:"digest"` + Signature SignatureSpec `json:"signature"` } diff --git a/bindings-go/apis/v2/signatures/sign.go b/bindings-go/apis/v2/signatures/sign.go index 1a34d408..d919cb5d 100644 --- a/bindings-go/apis/v2/signatures/sign.go +++ b/bindings-go/apis/v2/signatures/sign.go @@ -25,10 +25,10 @@ func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer, hasher H return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) } cd.Signatures = append(cd.Signatures, v2.Signature{ - Name: signatureName, - NormalisationType: v2.NormalisationTypeV1, - Digest: digest, - Signature: *signature, + Name: signatureName, + NormalisationVersion: v2.NormalisationVersionV1, + Digest: digest, + Signature: *signature, }) return nil } From 3b3889649ca7a4890fc656437e5f9276bf48f9a9 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 27 Oct 2021 14:42:48 +0200 Subject: [PATCH 08/36] adopt example to modified spec --- bindings-go/examples/signatures/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index d31a04f9..4be703a0 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -68,7 +68,7 @@ func main() { }, }, Signatures: []v2.Signature{ - v2.Signature{NormalisationType: v2.NormalisationTypeV1, + v2.Signature{NormalisationVersion: v2.NormalisationVersionV1, Digest: v2.DigestSpec{ Algorithm: "sha256", Value: "d782bbae5f6df38c1b7df79319ee6a9625dafcce5df3d730b25aee55db63fcfa", From 5c6836ee6c8f67694be121d82beb8571ac8cb309 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 4 Nov 2021 10:43:26 +0100 Subject: [PATCH 09/36] check public key to be a rsa public key --- bindings-go/apis/v2/signatures/rsa.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index 29cd67df..07d90a05 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -87,10 +87,14 @@ func CreateRsaVerifierFromKeyFile(pathToPublicKey string) (*RsaVerifier, error) if err != nil { return nil, fmt.Errorf("failed parsing key %w", err) } - key := untypedKey.(*rsa.PublicKey) - return &RsaVerifier{ - publicKey: *key, - }, nil + switch key := untypedKey.(type) { + case *rsa.PublicKey: + return &RsaVerifier{ + publicKey: *key, + }, nil + default: + return nil, fmt.Errorf("public key format is not supported. Only rsa.PublicKey is supported") + } } func (v RsaVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { From 8aa01b967b4e53b034a35625896efcdbce112640 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 4 Nov 2021 10:59:27 +0100 Subject: [PATCH 10/36] removed unused hash algorithms and todos --- bindings-go/apis/v2/signatures/rsa.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index 07d90a05..8d2b25c9 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -50,7 +50,7 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.Di return nil, fmt.Errorf("failed signing hash, %w", err) } return &v2.SignatureSpec{ - Algorithm: "RSASSA-PKCS1-V1_5-SIGN", //TODO: check + Algorithm: "RSASSA-PKCS1-V1_5-SIGN", Data: hex.EncodeToString(signature), }, nil } @@ -60,12 +60,6 @@ func hashAlgorithmLookup(algorithm string) (crypto.Hash, error) { switch strings.ToUpper(algorithm) { case "SHA256": return crypto.SHA256, nil - case "SHA512": - return crypto.SHA512, nil //TODO: test - case "SHA3_256": - return crypto.SHA3_256, nil // TODO: test - case "SHA3_512": - return crypto.SHA3_512, nil // TODO: test } return 0, fmt.Errorf("hash Algorithm %s not found", algorithm) } From 67a43fccce5457ed42084612c9d648fa71cdcf22 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 4 Nov 2021 12:40:18 +0100 Subject: [PATCH 11/36] omit signatures in json if empty --- bindings-go/apis/v2/componentdescriptor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 4f281d42..194a04ac 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -73,7 +73,7 @@ type ComponentDescriptor struct { ComponentSpec `json:"component"` // Signatures contains a list of signatures for the ComponentDescriptor - Signatures []Signature `json:"signatures"` + Signatures []Signature `json:"signatures,omitempty"` } // ComponentSpec defines a virtual component with From 8a942c207ad069e7e942a0026e21f306f55e2d19 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 4 Nov 2021 14:48:09 +0100 Subject: [PATCH 12/36] add tests --- .../apis/v2/signatures/normalize_test.go | 122 ++++++++++++++ bindings-go/apis/v2/signatures/rsa_test.go | 127 ++++++++++++++ bindings-go/apis/v2/signatures/sign_test.go | 158 ++++++++++++++++++ .../v2/signatures/signatures_suite_test.go | 13 ++ 4 files changed, 420 insertions(+) create mode 100644 bindings-go/apis/v2/signatures/normalize_test.go create mode 100644 bindings-go/apis/v2/signatures/rsa_test.go create mode 100644 bindings-go/apis/v2/signatures/sign_test.go create mode 100644 bindings-go/apis/v2/signatures/signatures_suite_test.go diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go new file mode 100644 index 00000000..e90445b1 --- /dev/null +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -0,0 +1,122 @@ +package signatures_test + +import ( + "crypto/sha256" + "encoding/hex" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Normalise/Hash component-descriptor", func() { + var baseCd v2.ComponentDescriptor + correctBaseCdHash := "dcefe8d7b4a43f5d7569234adbd696dfbfe3f1fc402ac3bed3f5e587624b9ac0" + //corresponding normalised CD: + //[{"component":[{"componentReferences":[[{"digest":[{"algorithm":"sha256"},{"value":"00000000000000"}]},{"extraIdentity":[{"refKey":"refName"}]},{"name":"compRefName"},{"version":"v0.0.2compRef"}]]},{"name":"CD-Name"},{"resources":[[{"digest":[{"algorithm":"sha256"},{"value":"00000000000000"}]},{"extraIdentity":[{"key":"value"}]},{"name":"Resource1"},{"version":"v0.0.3resource"}]]},{"version":"v0.0.1"}]},{"meta":[{"schemaVersion":"v2"}]}] + BeforeEach(func() { + baseCd = v2.ComponentDescriptor{ + Metadata: v2.Metadata{ + Version: "v2", + }, + ComponentSpec: v2.ComponentSpec{ + ObjectMeta: v2.ObjectMeta{ + Name: "CD-Name", + Version: "v0.0.1", + }, + ComponentReferences: []v2.ComponentReference{ + { + Name: "compRefName", + ComponentName: "compRefNameComponentName", + Version: "v0.0.2compRef", + ExtraIdentity: v2.Identity{ + "refKey": "refName", + }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000000000000", + }, + }, + }, + Resources: []v2.Resource{ + { + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "Resource1", + Version: "v0.0.3resource", + ExtraIdentity: v2.Identity{ + "key": "value", + }, + }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000000000000", + }, + }, + }, + }, + } + }) + + Describe("missing componentReference Digest", func() { + It("should fail to hash", func() { + baseCd.ComponentSpec.ComponentReferences[0].Digest = v2.DigestSpec{} + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(hash).To(BeNil()) + Expect(err).ToNot(BeNil()) + }) + }) + Describe("missing resource Digest", func() { + It("should fail to hash", func() { + baseCd.ComponentSpec.Resources[0].Digest = v2.DigestSpec{} + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(hash).To(BeNil()) + Expect(err).ToNot(BeNil()) + }) + }) + Describe("should give the correct hash hash", func() { + It("with sha256", func() { + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(err).To(BeNil()) + Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + }) + }) + Describe("should ignore modifications in unhashed fields", func() { + It("should succed with signature changes", func() { + baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ + Name: "TestSig", + NormalisationVersion: "v1", + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000", + }, + Signature: v2.SignatureSpec{ + Algorithm: "test", + Data: "0000", + }, + }) + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(err).To(BeNil()) + Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + }) + It("should succed with source changes", func() { + baseCd.Sources = append(baseCd.Sources, v2.Source{ + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "source1", + Version: "v0.0.0", + }, + }) + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(err).To(BeNil()) + Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + }) + It("should succed with resource access reference changes", func() { + access, err := v2.NewUnstructured(v2.NewOCIRegistryAccess("ociRef/path/to/image")) + Expect(err).To(BeNil()) + baseCd.Resources[0].Access = &access + hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + Expect(err).To(BeNil()) + Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + }) + }) +}) diff --git a/bindings-go/apis/v2/signatures/rsa_test.go b/bindings-go/apis/v2/signatures/rsa_test.go new file mode 100644 index 00000000..777c3388 --- /dev/null +++ b/bindings-go/apis/v2/signatures/rsa_test.go @@ -0,0 +1,127 @@ +package signatures_test + +import ( + "crypto/sha256" + "encoding/hex" + "io/ioutil" + "os" + "os/exec" + "path" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("RSA Sign/Verify", func() { + var pathPrivateKey string + var pathPublicKey string + var stringToHashAndSign string + var dir string + + BeforeEach(func() { + var err error + dir, err = ioutil.TempDir("", "component-spec-test") + Expect(err).To(BeNil()) + + // openssl genrsa -out private.key 4096 + pathPrivateKey = path.Join(dir, "private.key") + createPrivateKeyCommand := exec.Command("openssl", "genrsa", "-out", pathPrivateKey, "4096") + err = createPrivateKeyCommand.Run() + Expect(err).To(BeNil()) + + // openssl rsa -in private.key -outform PEM -pubout -out public.key + pathPublicKey = path.Join(dir, "public.key") + createPublicKeyCommand := exec.Command("openssl", "rsa", "-in", pathPrivateKey, "-outform", "PEM", "-pubout", "-out", pathPublicKey) + err = createPublicKeyCommand.Run() + Expect(err).To(BeNil()) + + stringToHashAndSign = "TestStringToSign" + }) + + AfterEach(func() { + os.RemoveAll(dir) + }) + + Describe("RSA Sign with private key", func() { + It("should create a signature", func() { + hashOfString := sha256.Sum256([]byte(stringToHashAndSign)) + + signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) + Expect(err).To(BeNil()) + signature, err := signer.Sign(v2.ComponentDescriptor{}, v2.DigestSpec{ + Algorithm: "sha256", + Value: hex.EncodeToString(hashOfString[:]), + }) + Expect(err).To(BeNil()) + Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) + Expect(signature.Data).NotTo(BeNil()) + }) + It("should should fail on unknown Digest algorithm", func() { + hashOfString := sha256.Sum256([]byte(stringToHashAndSign)) + + signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) + Expect(err).To(BeNil()) + signature, err := signer.Sign(v2.ComponentDescriptor{}, v2.DigestSpec{ + Algorithm: "unknown", + Value: hex.EncodeToString(hashOfString[:]), + }) + Expect(err).ToNot(BeNil()) + Expect(signature).To(BeNil()) + }) + + }) + Describe("RSA Sign verify public key", func() { + It("should verify a signature", func() { + hashOfString := sha256.Sum256([]byte(stringToHashAndSign)) + + signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) + Expect(err).To(BeNil()) + digest := v2.DigestSpec{ + Algorithm: "sha256", + Value: hex.EncodeToString(hashOfString[:]), + } + signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) + Expect(err).To(BeNil()) + Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) + Expect(signature.Data).NotTo(BeNil()) + + verifier, err := signatures.CreateRsaVerifierFromKeyFile(pathPublicKey) + Expect(err).To(BeNil()) + err = verifier.Verify(v2.ComponentDescriptor{}, v2.Signature{ + Digest: digest, + Signature: *signature, + }) + Expect(err).To(BeNil()) + }) + It("should deny a signature from a wrong actor", func() { + hashOfString := sha256.Sum256([]byte(stringToHashAndSign)) + + //generate a wrong key (e.g. from a malicious actor) + pathWrongPrivateKey := path.Join(dir, "privateWrong.key") + createWrongPrivateKeyCommand := exec.Command("openssl", "genrsa", "-out", pathWrongPrivateKey, "4096") + err := createWrongPrivateKeyCommand.Run() + Expect(err).To(BeNil()) + + signer, err := signatures.CreateRsaSignerFromKeyFile(pathWrongPrivateKey) + Expect(err).To(BeNil()) + digest := v2.DigestSpec{ + Algorithm: "sha256", + Value: hex.EncodeToString(hashOfString[:]), + } + signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) + Expect(err).To(BeNil()) + Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) + Expect(signature.Data).NotTo(BeNil()) + + verifier, err := signatures.CreateRsaVerifierFromKeyFile(pathPublicKey) + Expect(err).To(BeNil()) + err = verifier.Verify(v2.ComponentDescriptor{}, v2.Signature{ + Digest: digest, + Signature: *signature, + }) + Expect(err.Error()).To(BeIdenticalTo("signature verification failed, crypto/rsa: verification error")) + }) + }) +}) diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go new file mode 100644 index 00000000..8d44e7b3 --- /dev/null +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -0,0 +1,158 @@ +package signatures_test + +import ( + "crypto/sha256" + "fmt" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type TestSigner struct{} + +func (s TestSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) { + return &v2.SignatureSpec{ + Algorithm: "testSignAlgorithm", + Data: fmt.Sprintf("%s:%s-signed", digest.Algorithm, digest.Value), + }, nil +} + +type TestVerifier struct{} + +func (v TestVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { + if signature.Signature.Data != fmt.Sprintf("%s:%s-signed", signature.Digest.Algorithm, signature.Digest.Value) { + return fmt.Errorf("signature verification failed: Invalid signature") + } + return nil +} + +type TestSHA256Hasher signatures.Hasher + +var _ = Describe("Sign/Verify component-descriptor", func() { + var baseCd v2.ComponentDescriptor + testSHA256Hasher := signatures.Hasher{ + HashFunction: sha256.New(), + AlgorithmName: "sha256", + } + signatureName := "testSignatureName" + correctBaseCdHash := "dcefe8d7b4a43f5d7569234adbd696dfbfe3f1fc402ac3bed3f5e587624b9ac0" + + BeforeEach(func() { + baseCd = v2.ComponentDescriptor{ + Metadata: v2.Metadata{ + Version: "v2", + }, + ComponentSpec: v2.ComponentSpec{ + ObjectMeta: v2.ObjectMeta{ + Name: "CD-Name", + Version: "v0.0.1", + }, + ComponentReferences: []v2.ComponentReference{ + { + Name: "compRefName", + ComponentName: "compRefNameComponentName", + Version: "v0.0.2compRef", + ExtraIdentity: v2.Identity{ + "refKey": "refName", + }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000000000000", + }, + }, + }, + Resources: []v2.Resource{ + { + IdentityObjectMeta: v2.IdentityObjectMeta{ + Name: "Resource1", + Version: "v0.0.3resource", + ExtraIdentity: v2.Identity{ + "key": "value", + }, + }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000000000000", + }, + }, + }, + }, + } + }) + + Describe("sign component-descriptor", func() { + It("should add one signature", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + Expect(baseCd.Signatures[0].Name).To(BeIdenticalTo(signatureName)) + Expect(baseCd.Signatures[0].NormalisationVersion).To(BeIdenticalTo(v2.NormalisationVersionV1)) + Expect(baseCd.Signatures[0].Digest.Algorithm).To(BeIdenticalTo("sha256")) + Expect(baseCd.Signatures[0].Digest.Value).To(BeIdenticalTo(correctBaseCdHash)) + Expect(baseCd.Signatures[0].Signature.Algorithm).To(BeIdenticalTo("testSignAlgorithm")) + Expect(baseCd.Signatures[0].Signature.Data).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", "sha256", correctBaseCdHash))) + }) + }) + Describe("verify component-descriptor signature", func() { + It("should verify one signature", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).To(BeNil()) + }) + It("should reject an invalid signature", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + baseCd.Signatures[0].Signature.Data = "invalidSignature" + err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).ToNot(BeNil()) + }) + It("should reject a missing signature", func() { + err := signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).ToNot(BeNil()) + }) + + It("should validate the correct signature if multiple are present", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + + baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ + Name: "testSignAlgorithmNOTRight", + Digest: v2.DigestSpec{ + Algorithm: "testAlgorithm", + Value: "testValue", + }, + Signature: v2.SignatureSpec{ + Algorithm: "testSigning", + Data: "AdditionalSignature", + }, + }) + err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).To(BeNil()) + }) + }) + + Describe("verify normalised component-descriptor digest with signed digest ", func() { + It("should reject an invalid hash", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + baseCd.Signatures[0].Digest.Value = "invalidHash" + err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).ToNot(BeNil()) + }) + It("should reject a missing hash", func() { + err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) + Expect(err).To(BeNil()) + Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) + baseCd.Signatures[0].Digest = v2.DigestSpec{} + err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) + Expect(err).ToNot(BeNil()) + }) + }) +}) diff --git a/bindings-go/apis/v2/signatures/signatures_suite_test.go b/bindings-go/apis/v2/signatures/signatures_suite_test.go new file mode 100644 index 00000000..5da0e00d --- /dev/null +++ b/bindings-go/apis/v2/signatures/signatures_suite_test.go @@ -0,0 +1,13 @@ +package signatures_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestSignatures(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Signatures Test Suite") +} From 36b076e6c672f329657ce420cfda94a5c0f67d29 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 4 Nov 2021 14:48:23 +0100 Subject: [PATCH 13/36] improve example --- bindings-go/examples/signatures/main.go | 54 ++++++------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index 4be703a0..3812da06 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -16,69 +16,39 @@ func main() { }, ComponentSpec: v2.ComponentSpec{ ObjectMeta: v2.ObjectMeta{ - Name: "Name", - Version: "Version", + Name: "CD-Name", + Version: "v0.0.1", }, ComponentReferences: []v2.ComponentReference{ - v2.ComponentReference{ - Name: "refName", - ComponentName: "unimportantName", - Version: "v1ref", + { + Name: "compRefName", + ComponentName: "compRefNameComponentName", + Version: "v0.0.2compRef", ExtraIdentity: v2.Identity{ "refKey": "refName", }, Digest: v2.DigestSpec{ Algorithm: "sha256", - Value: "iuhadshksasad", + Value: "00000000000000", }, }, }, Resources: []v2.Resource{ - v2.Resource{ + { IdentityObjectMeta: v2.IdentityObjectMeta{ Name: "Resource1", - Version: "v1", + Version: "v0.0.3resource", ExtraIdentity: v2.Identity{ "key": "value", }, }, - }, - v2.Resource{ - IdentityObjectMeta: v2.IdentityObjectMeta{ - Name: "Resource2", - Version: "v2", - ExtraIdentity: v2.Identity{ - "zzz": "valuezzz", - "key": "value", - }, - }, - }, - }, - Sources: []v2.Source{ - v2.Source{ - IdentityObjectMeta: v2.IdentityObjectMeta{ - Name: "source1", - Version: "v0.1", - ExtraIdentity: v2.Identity{ - "key2": "value2", - "key1": "value1", - }, + Digest: v2.DigestSpec{ + Algorithm: "sha256", + Value: "00000000000000", }, }, }, }, - Signatures: []v2.Signature{ - v2.Signature{NormalisationVersion: v2.NormalisationVersionV1, - Digest: v2.DigestSpec{ - Algorithm: "sha256", - Value: "d782bbae5f6df38c1b7df79319ee6a9625dafcce5df3d730b25aee55db63fcfa", - }, - Signature: v2.SignatureSpec{ - Algorithm: "RSASSA-PKCS1-V1_5-SIGN", - Data: "3d7a81955e1b7cb9556fedb0886229f8b65f1b7ab2cc7be7c1dbe4acff79e2de7415eb1af26402b9d0dd8cdcb90fab212cf122c223bb502900674c90c251b91c044a864b057d1ec4672710ffa79198ab170746100fc2da3b7d78dbfd7a8260bd4adaaf6fccba0d01d7f0a371bdefce9a9792bb1ff4f3e48ad64e33d6a17609a3895d203e17b813ed4f4d4b5f1ef2a803bc2237e8021a92a3bc8780d2617553ece7dfb406af2e3f44000596968476d65b1a4f5e533692c0779823d56fff18c91a9a240a0efd76d012f17f3ecb37152f962590e049274652c634328f70d72ed78e602da182c274f6ded518be560f6846b57cf07227c90b19207b625466a31c0524dcb548433d626200e934a8ae50f987bfe4906fd1db2f1f0337f9ca18793625aedfa4eab2f4fc3b24fdf2c1a6cb2c2f8c4c7082c1b0f037c72d550c0f523349fc6061821d314586939507084ea441437b87152b58b521f1251f4be411e9e96236a385d76968316b4c2eace125417a43730c1bef02db8a0eb8a404352bd8390eda6b4af94681810122917dd59cf11249eb32c8d464f6f34cd7dbd5207efca4275bd20beb06f1afbb112b35980510f50bb4fa958a2426faad74511c48cf1d71abfb6b2bf7291bbb69fcb080a8461b3cd335ca259bd2e54e879d6884c3bf0473922171c018cfb8926ca2d9a0f79fb4618658f22fce7a56d55affd913618124a79e09", - }, - }, - }, } ctx := context.TODO() err := signatures.AddDigestsToComponentDescriptor(ctx, &cd, func(ctx context.Context, cd v2.ComponentDescriptor, cr v2.ComponentReference) (*v2.DigestSpec, error) { From a3c3fc2d0f1104960946aa0ea4932cb43ca8227d Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 10 Nov 2021 09:44:41 +0100 Subject: [PATCH 14/36] Feedback PR Review --- bindings-go/apis/v2/componentdescriptor.go | 5 +++-- bindings-go/apis/v2/signatures/normalize.go | 10 ++++------ bindings-go/apis/v2/signatures/normalize_test.go | 2 +- bindings-go/apis/v2/signatures/rsa.go | 12 ++++++++++-- bindings-go/apis/v2/signatures/rsa_test.go | 6 +++--- bindings-go/apis/v2/signatures/sign_test.go | 10 +++++----- bindings-go/apis/v2/signatures/types.go | 6 ++++++ 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 194a04ac..83f83f13 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -354,7 +354,7 @@ type SourceRef struct { type Resource struct { IdentityObjectMeta `json:",inline"` - // Digest is the optional digest of the referenced component. + // Digest is the optional digest of the referenced resource. // +optional Digest DigestSpec `json:"digest,omitempty"` @@ -451,9 +451,10 @@ type DigestSpec struct { // +k8s:openapi-gen=true type SignatureSpec struct { Algorithm string `json:"algorithm"` - Data string `json:"data"` + Value string `json:"value"` } +// NormalisationVersion versions the algorithm used for normalising the component-descriptor. type NormalisationVersion string const ( diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index ec36a025..e4ce4b4d 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -48,12 +48,10 @@ func HashForComponentDescriptor(cd v2.ComponentDescriptor, hashFunction hash.Has return nil, fmt.Errorf("failed normalising component descriptor %w", err) } hashFunction.Reset() - _, err = hashFunction.Write(normalisedComponentDescriptor) - if err != nil { - return nil, fmt.Errorf("failed hashing the normalisedComponentDescirptorJson: %w", err) + if _, err = hashFunction.Write(normalisedComponentDescriptor); err != nil { + return nil, fmt.Errorf("failed hashing the normalisedComponentDescriptorJson: %w", err) } - hash := hashFunction.Sum(nil) - return hash, nil + return hashFunction.Sum(nil), nil } func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { @@ -176,7 +174,7 @@ func getOnlyValueInEntry(entry Entry) interface{} { } // isNormaliseable checks if componentReferences and resources contain digest -// Does NOT verify if the digest are correct +// Does NOT verify if the digests are correct func isNormaliseable(cd v2.ComponentDescriptor) error { // check for digests on component references for _, reference := range cd.ComponentReferences { diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index e90445b1..6b29f408 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -92,7 +92,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }, Signature: v2.SignatureSpec{ Algorithm: "test", - Data: "0000", + Value: "0000", }, }) hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index 8d2b25c9..c2fca8d6 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -13,10 +13,13 @@ import ( v2 "github.com/gardener/component-spec/bindings-go/apis/v2" ) +// RsaSigner is a signatures.Signer compatible struct to sign with RSASSA-PKCS1-V1_5-SIGN. type RsaSigner struct { privateKey rsa.PrivateKey } +// CreateRsaSignerFromKeyFile creates an Instance of RsaSigner with the given private key. +// The private key has to be in the PKCS #1, ASN.1 DER form, see x509.ParsePKCS1PrivateKey. func CreateRsaSignerFromKeyFile(pathToPrivateKey string) (*RsaSigner, error) { privKeyFile, err := ioutil.ReadFile(pathToPrivateKey) if err != nil { @@ -36,6 +39,7 @@ func CreateRsaSignerFromKeyFile(pathToPrivateKey string) (*RsaSigner, error) { }, nil } +// Sign returns the signature for the data for the component-descriptor. func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) { decodedHash, err := hex.DecodeString(digest.Value) if err != nil { @@ -51,7 +55,7 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.Di } return &v2.SignatureSpec{ Algorithm: "RSASSA-PKCS1-V1_5-SIGN", - Data: hex.EncodeToString(signature), + Value: hex.EncodeToString(signature), }, nil } @@ -64,10 +68,13 @@ func hashAlgorithmLookup(algorithm string) (crypto.Hash, error) { return 0, fmt.Errorf("hash Algorithm %s not found", algorithm) } +// RsaVerifier is a signatures.Verifier compatible struct to verify RSASSA-PKCS1-V1_5-SIGN signatures. type RsaVerifier struct { publicKey rsa.PublicKey } +// CreateRsaVerifierFromKeyFile creates an Instance of RsaVerifier with the given rsa public key. +// The private key has to be in the PKIX, ASN.1 DER form, see x509.ParsePKIXPublicKey. func CreateRsaVerifierFromKeyFile(pathToPublicKey string) (*RsaVerifier, error) { publicKey, err := ioutil.ReadFile(pathToPublicKey) if err != nil { @@ -91,12 +98,13 @@ func CreateRsaVerifierFromKeyFile(pathToPublicKey string) (*RsaVerifier, error) } } +// Verify checks the signature, returns an error on verification failure func (v RsaVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { decodedHash, err := hex.DecodeString(signature.Digest.Value) if err != nil { return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) } - decodedSignature, err := hex.DecodeString(signature.Signature.Data) + decodedSignature, err := hex.DecodeString(signature.Signature.Value) if err != nil { return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) } diff --git a/bindings-go/apis/v2/signatures/rsa_test.go b/bindings-go/apis/v2/signatures/rsa_test.go index 777c3388..1b230467 100644 --- a/bindings-go/apis/v2/signatures/rsa_test.go +++ b/bindings-go/apis/v2/signatures/rsa_test.go @@ -56,7 +56,7 @@ var _ = Describe("RSA Sign/Verify", func() { }) Expect(err).To(BeNil()) Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) - Expect(signature.Data).NotTo(BeNil()) + Expect(signature.Value).NotTo(BeNil()) }) It("should should fail on unknown Digest algorithm", func() { hashOfString := sha256.Sum256([]byte(stringToHashAndSign)) @@ -85,7 +85,7 @@ var _ = Describe("RSA Sign/Verify", func() { signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) Expect(err).To(BeNil()) Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) - Expect(signature.Data).NotTo(BeNil()) + Expect(signature.Value).NotTo(BeNil()) verifier, err := signatures.CreateRsaVerifierFromKeyFile(pathPublicKey) Expect(err).To(BeNil()) @@ -113,7 +113,7 @@ var _ = Describe("RSA Sign/Verify", func() { signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) Expect(err).To(BeNil()) Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) - Expect(signature.Data).NotTo(BeNil()) + Expect(signature.Value).NotTo(BeNil()) verifier, err := signatures.CreateRsaVerifierFromKeyFile(pathPublicKey) Expect(err).To(BeNil()) diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index 8d44e7b3..14e6af54 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -15,14 +15,14 @@ type TestSigner struct{} func (s TestSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) { return &v2.SignatureSpec{ Algorithm: "testSignAlgorithm", - Data: fmt.Sprintf("%s:%s-signed", digest.Algorithm, digest.Value), + Value: fmt.Sprintf("%s:%s-signed", digest.Algorithm, digest.Value), }, nil } type TestVerifier struct{} func (v TestVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { - if signature.Signature.Data != fmt.Sprintf("%s:%s-signed", signature.Digest.Algorithm, signature.Digest.Value) { + if signature.Signature.Value != fmt.Sprintf("%s:%s-signed", signature.Digest.Algorithm, signature.Digest.Value) { return fmt.Errorf("signature verification failed: Invalid signature") } return nil @@ -92,7 +92,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { Expect(baseCd.Signatures[0].Digest.Algorithm).To(BeIdenticalTo("sha256")) Expect(baseCd.Signatures[0].Digest.Value).To(BeIdenticalTo(correctBaseCdHash)) Expect(baseCd.Signatures[0].Signature.Algorithm).To(BeIdenticalTo("testSignAlgorithm")) - Expect(baseCd.Signatures[0].Signature.Data).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", "sha256", correctBaseCdHash))) + Expect(baseCd.Signatures[0].Signature.Value).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", "sha256", correctBaseCdHash))) }) }) Describe("verify component-descriptor signature", func() { @@ -107,7 +107,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { err := signatures.SignComponentDescriptor(&baseCd, TestSigner{}, testSHA256Hasher, signatureName) Expect(err).To(BeNil()) Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) - baseCd.Signatures[0].Signature.Data = "invalidSignature" + baseCd.Signatures[0].Signature.Value = "invalidSignature" err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) Expect(err).ToNot(BeNil()) }) @@ -129,7 +129,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { }, Signature: v2.SignatureSpec{ Algorithm: "testSigning", - Data: "AdditionalSignature", + Value: "AdditionalSignature", }, }) err = signatures.VerifySignedComponentDescriptor(&baseCd, TestVerifier{}, signatureName) diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index 34931683..0c16156e 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -9,21 +9,27 @@ import ( v2 "github.com/gardener/component-spec/bindings-go/apis/v2" ) +// Signer interface is used to implement different signing algorithms. +// Each Signer should have a matching Verifier. type Signer interface { // Sign returns the signature for the data for the component-descriptor Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) } +// Verifier interface is used to implement different verification algorithms. +// Each Verifier should have a matching Signer. type Verifier interface { // Verify checks the signature, returns an error on verification failure Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error } +// Hasher encapsulates a hash.Hash interface with an algorithm name. type Hasher struct { HashFunction hash.Hash AlgorithmName string } +// HasherForName creates a Hasher instance for the algorithmName. func HasherForName(algorithmName string) (*Hasher, error) { switch strings.ToUpper(algorithmName) { case "SHA256": From 0e3c2bda9721372d32c2f3e07404960abf51354f Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 10 Nov 2021 09:46:59 +0100 Subject: [PATCH 15/36] make format --- bindings-go/apis/v2/signatures/normalize_test.go | 5 +++-- bindings-go/apis/v2/signatures/rsa_test.go | 5 +++-- bindings-go/apis/v2/signatures/sign_test.go | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index 6b29f408..24576ad9 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -4,10 +4,11 @@ import ( "crypto/sha256" "encoding/hex" - v2 "github.com/gardener/component-spec/bindings-go/apis/v2" - "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" ) var _ = Describe("Normalise/Hash component-descriptor", func() { diff --git a/bindings-go/apis/v2/signatures/rsa_test.go b/bindings-go/apis/v2/signatures/rsa_test.go index 1b230467..0dcf2bb9 100644 --- a/bindings-go/apis/v2/signatures/rsa_test.go +++ b/bindings-go/apis/v2/signatures/rsa_test.go @@ -8,10 +8,11 @@ import ( "os/exec" "path" - v2 "github.com/gardener/component-spec/bindings-go/apis/v2" - "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" ) var _ = Describe("RSA Sign/Verify", func() { diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index 14e6af54..63a505a2 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -4,10 +4,11 @@ import ( "crypto/sha256" "fmt" - v2 "github.com/gardener/component-spec/bindings-go/apis/v2" - "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + + v2 "github.com/gardener/component-spec/bindings-go/apis/v2" + "github.com/gardener/component-spec/bindings-go/apis/v2/signatures" ) type TestSigner struct{} From d0799b8637a64f51f80428e50f2f143d2ca656b1 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 10 Nov 2021 10:01:45 +0100 Subject: [PATCH 16/36] deepSort fails on unknown type, so does normalising --- bindings-go/apis/v2/signatures/normalize.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index e4ce4b4d..62c5ec83 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -111,7 +111,9 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { {"component": componentSpec}, } - deepSort(normalizedComponentDescriptor) + if err := deepSort(normalizedComponentDescriptor); err != nil { + return nil, fmt.Errorf("failed sorting during normalisation: %w", err) + } normalizedJson, err := json.Marshal(normalizedComponentDescriptor) @@ -131,13 +133,15 @@ func buildExtraIdentity(identity v2.Identity) []Entry { } // deepSort sorts Entry, []Enry and [][]Entry interfaces recursively, lexicographicly by key(Entry). -func deepSort(in interface{}) { +func deepSort(in interface{}) error { switch castIn := in.(type) { case []Entry: // sort the values recursively for every entry for _, entry := range castIn { val := getOnlyValueInEntry(entry) - deepSort(val) + if err := deepSort(val); err != nil { + return err + } } // sort the entries based on the key sort.SliceStable(castIn, func(i, j int) bool { @@ -145,16 +149,21 @@ func deepSort(in interface{}) { }) case Entry: val := getOnlyValueInEntry(castIn) - deepSort(val) + if err := deepSort(val); err != nil { + return err + } case [][]Entry: for _, v := range castIn { - deepSort(v) + if err := deepSort(v); err != nil { + return err + } } case string: break default: - fmt.Println("unknow type") + return fmt.Errorf("unknown type in sorting. This should not happen") } + return nil } func getOnlyKeyInEntry(entry Entry) string { From b2d695ac62afb5e41f835e61e0e5d3ab5427cffe Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 10 Nov 2021 10:40:06 +0100 Subject: [PATCH 17/36] change digest to pointer allows ommiting a digest by setting it nil --- bindings-go/apis/v2/componentdescriptor.go | 4 ++-- bindings-go/apis/v2/signatures/normalize.go | 12 ++++++------ bindings-go/apis/v2/signatures/normalize_test.go | 8 ++++---- bindings-go/apis/v2/signatures/sign_test.go | 4 ++-- bindings-go/examples/signatures/main.go | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 83f83f13..5fead14b 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -356,7 +356,7 @@ type Resource struct { // Digest is the optional digest of the referenced resource. // +optional - Digest DigestSpec `json:"digest,omitempty"` + Digest *DigestSpec `json:"digest,omitempty"` // Relation describes the relation of the resource to the component. // Can be a local or external resource @@ -386,7 +386,7 @@ type ComponentReference struct { ExtraIdentity Identity `json:"extraIdentity,omitempty"` // Digest is the optional digest of the referenced component. // +optional - Digest DigestSpec `json:"digest,omitempty"` + Digest *DigestSpec `json:"digest,omitempty"` // Labels defines an optional set of additional labels // describing the object. // +optional diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 62c5ec83..dfcd7177 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -19,22 +19,22 @@ func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescri resResolver func(context.Context, v2.ComponentDescriptor, v2.Resource) (*v2.DigestSpec, error)) error { for i, reference := range cd.ComponentReferences { - if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + if reference.Digest == nil || reference.Digest.Algorithm == "" || reference.Digest.Value == "" { digest, err := compRefResolver(ctx, *cd, reference) if err != nil { return fmt.Errorf("failed resolving componentReference for %s:%s: %w", reference.Name, reference.Version, err) } - cd.ComponentReferences[i].Digest = *digest + cd.ComponentReferences[i].Digest = digest } } for i, res := range cd.Resources { - if res.Digest.Algorithm == "" || res.Digest.Value == "" { + if res.Digest == nil || res.Digest.Algorithm == "" || res.Digest.Value == "" { digest, err := resResolver(ctx, *cd, res) if err != nil { return fmt.Errorf("failed resolving resource for %s:%s: %w", res.Name, res.Version, err) } - cd.Resources[i].Digest = *digest + cd.Resources[i].Digest = digest } } return nil @@ -187,14 +187,14 @@ func getOnlyValueInEntry(entry Entry) interface{} { func isNormaliseable(cd v2.ComponentDescriptor) error { // check for digests on component references for _, reference := range cd.ComponentReferences { - if reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + if reference.Digest == nil || reference.Digest.Algorithm == "" || reference.Digest.Value == "" { return fmt.Errorf("missing digest in componentReference for %s:%s", reference.Name, reference.Version) } } // check for digests on resources for _, res := range cd.Resources { - if res.Digest.Algorithm == "" || res.Digest.Value == "" { + if res.Digest == nil || res.Digest.Algorithm == "" || res.Digest.Value == "" { return fmt.Errorf("missing digest in resource for %s:%s", res.Name, res.Version) } } diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index 24576ad9..f6e03b95 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -34,7 +34,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { ExtraIdentity: v2.Identity{ "refKey": "refName", }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, @@ -49,7 +49,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { "key": "value", }, }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, @@ -61,7 +61,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Describe("missing componentReference Digest", func() { It("should fail to hash", func() { - baseCd.ComponentSpec.ComponentReferences[0].Digest = v2.DigestSpec{} + baseCd.ComponentSpec.ComponentReferences[0].Digest = nil hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) Expect(hash).To(BeNil()) Expect(err).ToNot(BeNil()) @@ -69,7 +69,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }) Describe("missing resource Digest", func() { It("should fail to hash", func() { - baseCd.ComponentSpec.Resources[0].Digest = v2.DigestSpec{} + baseCd.ComponentSpec.Resources[0].Digest = nil hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) Expect(hash).To(BeNil()) Expect(err).ToNot(BeNil()) diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index 63a505a2..ea3f185b 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -58,7 +58,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { ExtraIdentity: v2.Identity{ "refKey": "refName", }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, @@ -73,7 +73,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { "key": "value", }, }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index 3812da06..acd2ac3e 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -27,7 +27,7 @@ func main() { ExtraIdentity: v2.Identity{ "refKey": "refName", }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, @@ -42,7 +42,7 @@ func main() { "key": "value", }, }, - Digest: v2.DigestSpec{ + Digest: &v2.DigestSpec{ Algorithm: "sha256", Value: "00000000000000", }, From 507e1a61ef592c77cf68457043a2df937b332045 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 9 Dec 2021 10:07:46 +0100 Subject: [PATCH 18/36] make SelectSignatureByName public --- bindings-go/apis/v2/signatures/sign.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bindings-go/apis/v2/signatures/sign.go b/bindings-go/apis/v2/signatures/sign.go index d919cb5d..eb5d0a98 100644 --- a/bindings-go/apis/v2/signatures/sign.go +++ b/bindings-go/apis/v2/signatures/sign.go @@ -38,7 +38,7 @@ func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer, hasher H func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifier, signatureName string) error { //find matching signature - matchingSignature, err := selectSignatureByName(cd, signatureName) + matchingSignature, err := SelectSignatureByName(cd, signatureName) if err != nil { return fmt.Errorf("failed checking signature: %w", err) } @@ -67,7 +67,8 @@ func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifi return nil } -func selectSignatureByName(cd *v2.ComponentDescriptor, signatureName string) (*v2.Signature, error) { +// SelectSignatureByName returns the Signature (Digest and SigantureSpec) matching the given name +func SelectSignatureByName(cd *v2.ComponentDescriptor, signatureName string) (*v2.Signature, error) { for _, signature := range cd.Signatures { if signature.Name == signatureName { return &signature, nil From 9435823677b6ba5be8c1295f492b96498386ddd8 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 9 Dec 2021 10:56:17 +0100 Subject: [PATCH 19/36] update to latest spec changes --- bindings-go/apis/v2/componentdescriptor.go | 19 ++++--- bindings-go/apis/v2/signatures/normalize.go | 28 +++++---- .../apis/v2/signatures/normalize_test.go | 57 +++++++++++-------- bindings-go/apis/v2/signatures/rsa.go | 6 +- bindings-go/apis/v2/signatures/rsa_test.go | 20 ++++--- bindings-go/apis/v2/signatures/sign.go | 25 +++----- bindings-go/apis/v2/signatures/sign_test.go | 25 ++++---- bindings-go/examples/signatures/main.go | 25 ++++---- 8 files changed, 113 insertions(+), 92 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 5fead14b..e2c7877a 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -442,8 +442,9 @@ func (o *ComponentReference) GetIdentityDigest() []byte { // +k8s:deepcopy-gen=true // +k8s:openapi-gen=true type DigestSpec struct { - Algorithm string `json:"algorithm"` - Value string `json:"value"` + HashAlgorithm string `json:"hashAlgorithm"` + NormalisationAlgorithm string `json:"normalisationAlgorithm"` + Value string `json:"value"` } // SignatureSpec defines the signature and algorithm. @@ -454,19 +455,19 @@ type SignatureSpec struct { Value string `json:"value"` } -// NormalisationVersion versions the algorithm used for normalising the component-descriptor. -type NormalisationVersion string +// NormalisationAlgorithm types and versions the algorithm used for digest generation. +type NormalisationAlgorithm string const ( - NormalisationVersionV1 NormalisationVersion = "v1" + JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisationV1" + ManifestDigestV1 NormalisationAlgorithm = "manifestDigestV1" ) // Signature defines a digest and corresponding signature, identifyable by name. // +k8s:deepcopy-gen=true // +k8s:openapi-gen=true type Signature struct { - Name string `json:"name"` - NormalisationVersion NormalisationVersion `json:"normalisationVersion"` - Digest DigestSpec `json:"digest"` - Signature SignatureSpec `json:"signature"` + Name string `json:"name"` + Digest DigestSpec `json:"digest"` + Signature SignatureSpec `json:"signature"` } diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index dfcd7177..265e74fe 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -2,9 +2,9 @@ package signatures import ( "context" + "encoding/hex" "encoding/json" "fmt" - "hash" "sort" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" @@ -19,7 +19,7 @@ func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescri resResolver func(context.Context, v2.ComponentDescriptor, v2.Resource) (*v2.DigestSpec, error)) error { for i, reference := range cd.ComponentReferences { - if reference.Digest == nil || reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + if reference.Digest == nil || reference.Digest.HashAlgorithm == "" || reference.Digest.NormalisationAlgorithm == "" || reference.Digest.Value == "" { digest, err := compRefResolver(ctx, *cd, reference) if err != nil { return fmt.Errorf("failed resolving componentReference for %s:%s: %w", reference.Name, reference.Version, err) @@ -29,7 +29,7 @@ func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescri } for i, res := range cd.Resources { - if res.Digest == nil || res.Digest.Algorithm == "" || res.Digest.Value == "" { + if res.Digest == nil || res.Digest.HashAlgorithm == "" || res.Digest.NormalisationAlgorithm == "" || res.Digest.Value == "" { digest, err := resResolver(ctx, *cd, res) if err != nil { return fmt.Errorf("failed resolving resource for %s:%s: %w", res.Name, res.Version, err) @@ -42,16 +42,20 @@ func AddDigestsToComponentDescriptor(ctx context.Context, cd *v2.ComponentDescri // HashForComponentDescriptor return the hash for the component-descriptor, if it is normaliseable // (= componentReferences and resources contain digest field) -func HashForComponentDescriptor(cd v2.ComponentDescriptor, hashFunction hash.Hash) ([]byte, error) { +func HashForComponentDescriptor(cd v2.ComponentDescriptor, hash Hasher) (*v2.DigestSpec, error) { normalisedComponentDescriptor, err := normalizeComponentDescriptor(cd) if err != nil { return nil, fmt.Errorf("failed normalising component descriptor %w", err) } - hashFunction.Reset() - if _, err = hashFunction.Write(normalisedComponentDescriptor); err != nil { + hash.HashFunction.Reset() + if _, err = hash.HashFunction.Write(normalisedComponentDescriptor); err != nil { return nil, fmt.Errorf("failed hashing the normalisedComponentDescriptorJson: %w", err) } - return hashFunction.Sum(nil), nil + return &v2.DigestSpec{ + HashAlgorithm: hash.AlgorithmName, + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: hex.EncodeToString(hash.HashFunction.Sum(nil)), + }, nil } func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { @@ -68,7 +72,8 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { extraIdentity := buildExtraIdentity(ref.ExtraIdentity) digest := []Entry{ - {"algorithm": ref.Digest.Algorithm}, + {"hashAlgorithm": ref.Digest.HashAlgorithm}, + {"normalisationAlgorithm": ref.Digest.NormalisationAlgorithm}, {"value": ref.Digest.Value}, } @@ -86,7 +91,8 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { extraIdentity := buildExtraIdentity(res.ExtraIdentity) digest := []Entry{ - {"algorithm": res.Digest.Algorithm}, + {"hashAlgorithm": res.Digest.HashAlgorithm}, + {"normalisationAlgorithm": res.Digest.NormalisationAlgorithm}, {"value": res.Digest.Value}, } @@ -187,14 +193,14 @@ func getOnlyValueInEntry(entry Entry) interface{} { func isNormaliseable(cd v2.ComponentDescriptor) error { // check for digests on component references for _, reference := range cd.ComponentReferences { - if reference.Digest == nil || reference.Digest.Algorithm == "" || reference.Digest.Value == "" { + if reference.Digest == nil || reference.Digest.HashAlgorithm == "" || reference.Digest.NormalisationAlgorithm == "" || reference.Digest.Value == "" { return fmt.Errorf("missing digest in componentReference for %s:%s", reference.Name, reference.Version) } } // check for digests on resources for _, res := range cd.Resources { - if res.Digest == nil || res.Digest.Algorithm == "" || res.Digest.Value == "" { + if res.Digest == nil || res.Digest.HashAlgorithm == "" || res.Digest.NormalisationAlgorithm == "" || res.Digest.Value == "" { return fmt.Errorf("missing digest in resource for %s:%s", res.Name, res.Version) } } diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index f6e03b95..a8633aa6 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -1,9 +1,6 @@ package signatures_test import ( - "crypto/sha256" - "encoding/hex" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -13,9 +10,9 @@ import ( var _ = Describe("Normalise/Hash component-descriptor", func() { var baseCd v2.ComponentDescriptor - correctBaseCdHash := "dcefe8d7b4a43f5d7569234adbd696dfbfe3f1fc402ac3bed3f5e587624b9ac0" + correctBaseCdHash := "64c04405dcd03a6f345584adb860cad4f4ed6dba1943d5535db3407b2bf9f000" //corresponding normalised CD: - //[{"component":[{"componentReferences":[[{"digest":[{"algorithm":"sha256"},{"value":"00000000000000"}]},{"extraIdentity":[{"refKey":"refName"}]},{"name":"compRefName"},{"version":"v0.0.2compRef"}]]},{"name":"CD-Name"},{"resources":[[{"digest":[{"algorithm":"sha256"},{"value":"00000000000000"}]},{"extraIdentity":[{"key":"value"}]},{"name":"Resource1"},{"version":"v0.0.3resource"}]]},{"version":"v0.0.1"}]},{"meta":[{"schemaVersion":"v2"}]}] + //[{"component":[{"componentReferences":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"jsonNormalisationV1"},{"value":"00000000000000"}]},{"extraIdentity":[{"refKey":"refName"}]},{"name":"compRefName"},{"version":"v0.0.2compRef"}]]},{"name":"CD-Name"},{"resources":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"manifestDigestV1"},{"value":"00000000000000"}]},{"extraIdentity":[{"key":"value"}]},{"name":"Resource1"},{"version":"v0.0.3resource"}]]},{"version":"v0.0.1"}]},{"meta":[{"schemaVersion":"v2"}]}] BeforeEach(func() { baseCd = v2.ComponentDescriptor{ Metadata: v2.Metadata{ @@ -35,8 +32,9 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: "00000000000000", }, }, }, @@ -50,8 +48,9 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }, }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.ManifestDigestV1), + Value: "00000000000000", }, }, }, @@ -62,7 +61,9 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Describe("missing componentReference Digest", func() { It("should fail to hash", func() { baseCd.ComponentSpec.ComponentReferences[0].Digest = nil - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") + Expect(err).To(BeNil()) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(hash).To(BeNil()) Expect(err).ToNot(BeNil()) }) @@ -70,35 +71,41 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Describe("missing resource Digest", func() { It("should fail to hash", func() { baseCd.ComponentSpec.Resources[0].Digest = nil - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") + Expect(err).To(BeNil()) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(hash).To(BeNil()) Expect(err).ToNot(BeNil()) }) }) Describe("should give the correct hash hash", func() { It("with sha256", func() { - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") + Expect(err).To(BeNil()) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) - Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + Expect(hash.Value).To(Equal(correctBaseCdHash)) }) }) Describe("should ignore modifications in unhashed fields", func() { It("should succed with signature changes", func() { baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ - Name: "TestSig", - NormalisationVersion: "v1", + Name: "TestSig", Digest: v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: "00000", }, Signature: v2.SignatureSpec{ Algorithm: "test", Value: "0000", }, }) - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") Expect(err).To(BeNil()) - Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) + Expect(err).To(BeNil()) + Expect(hash.Value).To(Equal(correctBaseCdHash)) }) It("should succed with source changes", func() { baseCd.Sources = append(baseCd.Sources, v2.Source{ @@ -107,17 +114,21 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Version: "v0.0.0", }, }) - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") + Expect(err).To(BeNil()) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) - Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + Expect(hash.Value).To(Equal(correctBaseCdHash)) }) It("should succed with resource access reference changes", func() { access, err := v2.NewUnstructured(v2.NewOCIRegistryAccess("ociRef/path/to/image")) Expect(err).To(BeNil()) baseCd.Resources[0].Access = &access - hash, err := signatures.HashForComponentDescriptor(baseCd, sha256.New()) + hasher, err := signatures.HasherForName("sha256") + Expect(err).To(BeNil()) + hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) - Expect(hex.EncodeToString(hash)).To(Equal(correctBaseCdHash)) + Expect(hash.Value).To(Equal(correctBaseCdHash)) }) }) }) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index c2fca8d6..917be1fd 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -45,7 +45,7 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.Di if err != nil { return nil, fmt.Errorf("failed decoding hash to bytes") } - hashType, err := hashAlgorithmLookup(digest.Algorithm) + hashType, err := hashAlgorithmLookup(digest.HashAlgorithm) if err != nil { return nil, fmt.Errorf("failed looking up hash algorithm") } @@ -108,9 +108,9 @@ func (v RsaVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signatur if err != nil { return fmt.Errorf("failed decoding hash %s: %w", signature.Digest.Value, err) } - algorithm, err := hashAlgorithmLookup(signature.Digest.Algorithm) + algorithm, err := hashAlgorithmLookup(signature.Digest.HashAlgorithm) if err != nil { - return fmt.Errorf("failed looking up hash algorithm for %s: %w", signature.Digest.Algorithm, err) + return fmt.Errorf("failed looking up hash algorithm for %s: %w", signature.Digest.HashAlgorithm, err) } err = rsa.VerifyPKCS1v15(&v.publicKey, algorithm, decodedHash, decodedSignature) if err != nil { diff --git a/bindings-go/apis/v2/signatures/rsa_test.go b/bindings-go/apis/v2/signatures/rsa_test.go index 0dcf2bb9..5143086f 100644 --- a/bindings-go/apis/v2/signatures/rsa_test.go +++ b/bindings-go/apis/v2/signatures/rsa_test.go @@ -52,8 +52,9 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) Expect(err).To(BeNil()) signature, err := signer.Sign(v2.ComponentDescriptor{}, v2.DigestSpec{ - Algorithm: "sha256", - Value: hex.EncodeToString(hashOfString[:]), + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: hex.EncodeToString(hashOfString[:]), }) Expect(err).To(BeNil()) Expect(signature.Algorithm).To(BeIdenticalTo("RSASSA-PKCS1-V1_5-SIGN")) @@ -65,8 +66,9 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) Expect(err).To(BeNil()) signature, err := signer.Sign(v2.ComponentDescriptor{}, v2.DigestSpec{ - Algorithm: "unknown", - Value: hex.EncodeToString(hashOfString[:]), + HashAlgorithm: "unknown", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: hex.EncodeToString(hashOfString[:]), }) Expect(err).ToNot(BeNil()) Expect(signature).To(BeNil()) @@ -80,8 +82,9 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) Expect(err).To(BeNil()) digest := v2.DigestSpec{ - Algorithm: "sha256", - Value: hex.EncodeToString(hashOfString[:]), + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: hex.EncodeToString(hashOfString[:]), } signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) Expect(err).To(BeNil()) @@ -108,8 +111,9 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathWrongPrivateKey) Expect(err).To(BeNil()) digest := v2.DigestSpec{ - Algorithm: "sha256", - Value: hex.EncodeToString(hashOfString[:]), + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: hex.EncodeToString(hashOfString[:]), } signature, err := signer.Sign(v2.ComponentDescriptor{}, digest) Expect(err).To(BeNil()) diff --git a/bindings-go/apis/v2/signatures/sign.go b/bindings-go/apis/v2/signatures/sign.go index eb5d0a98..6e161f04 100644 --- a/bindings-go/apis/v2/signatures/sign.go +++ b/bindings-go/apis/v2/signatures/sign.go @@ -1,7 +1,6 @@ package signatures import ( - "encoding/hex" "fmt" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" @@ -10,25 +9,19 @@ import ( // SignComponentDescriptor signs the given component-descriptor with the signer. // The component-descriptor has to contain digests for componentReferences and resources. func SignComponentDescriptor(cd *v2.ComponentDescriptor, signer Signer, hasher Hasher, signatureName string) error { - hashCd, err := HashForComponentDescriptor(*cd, hasher.HashFunction) + hashedDigest, err := HashForComponentDescriptor(*cd, hasher) if err != nil { return fmt.Errorf("failed getting hash for cd: %w", err) } - digest := v2.DigestSpec{ - Algorithm: hasher.AlgorithmName, - Value: hex.EncodeToString(hashCd), - } - - signature, err := signer.Sign(*cd, digest) + signature, err := signer.Sign(*cd, *hashedDigest) if err != nil { return fmt.Errorf("failed signing hash of normalised component descriptor, %w", err) } cd.Signatures = append(cd.Signatures, v2.Signature{ - Name: signatureName, - NormalisationVersion: v2.NormalisationVersionV1, - Digest: digest, - Signature: *signature, + Name: signatureName, + Digest: *hashedDigest, + Signature: *signature, }) return nil } @@ -50,17 +43,17 @@ func VerifySignedComponentDescriptor(cd *v2.ComponentDescriptor, verifier Verifi } //get hasher by algorithm name - hasher, err := HasherForName(matchingSignature.Digest.Algorithm) + hasher, err := HasherForName(matchingSignature.Digest.HashAlgorithm) if err != nil { - return fmt.Errorf("failed creating hasher for %s: %w", matchingSignature.Digest.Algorithm, err) + return fmt.Errorf("failed creating hasher for %s: %w", matchingSignature.Digest.HashAlgorithm, err) } //Verify normalised cd to given (and verified) hash - hashCd, err := HashForComponentDescriptor(*cd, hasher.HashFunction) + hashCd, err := HashForComponentDescriptor(*cd, *hasher) if err != nil { return fmt.Errorf("failed getting hash for cd: %w", err) } - if hex.EncodeToString(hashCd) != matchingSignature.Digest.Value { + if hashCd.Value != matchingSignature.Digest.Value { return fmt.Errorf("normalised component-descriptor does not match signed hash") } diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index ea3f185b..dc985071 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -16,14 +16,14 @@ type TestSigner struct{} func (s TestSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.DigestSpec) (*v2.SignatureSpec, error) { return &v2.SignatureSpec{ Algorithm: "testSignAlgorithm", - Value: fmt.Sprintf("%s:%s-signed", digest.Algorithm, digest.Value), + Value: fmt.Sprintf("%s:%s-signed", digest.HashAlgorithm, digest.Value), }, nil } type TestVerifier struct{} func (v TestVerifier) Verify(componentDescriptor v2.ComponentDescriptor, signature v2.Signature) error { - if signature.Signature.Value != fmt.Sprintf("%s:%s-signed", signature.Digest.Algorithm, signature.Digest.Value) { + if signature.Signature.Value != fmt.Sprintf("%s:%s-signed", signature.Digest.HashAlgorithm, signature.Digest.Value) { return fmt.Errorf("signature verification failed: Invalid signature") } return nil @@ -38,7 +38,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { AlgorithmName: "sha256", } signatureName := "testSignatureName" - correctBaseCdHash := "dcefe8d7b4a43f5d7569234adbd696dfbfe3f1fc402ac3bed3f5e587624b9ac0" + correctBaseCdHash := "64c04405dcd03a6f345584adb860cad4f4ed6dba1943d5535db3407b2bf9f000" BeforeEach(func() { baseCd = v2.ComponentDescriptor{ @@ -59,8 +59,9 @@ var _ = Describe("Sign/Verify component-descriptor", func() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: "00000000000000", }, }, }, @@ -74,8 +75,9 @@ var _ = Describe("Sign/Verify component-descriptor", func() { }, }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.ManifestDigestV1), + Value: "00000000000000", }, }, }, @@ -89,8 +91,8 @@ var _ = Describe("Sign/Verify component-descriptor", func() { Expect(err).To(BeNil()) Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) Expect(baseCd.Signatures[0].Name).To(BeIdenticalTo(signatureName)) - Expect(baseCd.Signatures[0].NormalisationVersion).To(BeIdenticalTo(v2.NormalisationVersionV1)) - Expect(baseCd.Signatures[0].Digest.Algorithm).To(BeIdenticalTo("sha256")) + Expect(baseCd.Signatures[0].Digest.NormalisationAlgorithm).To(BeIdenticalTo(string(v2.JsonNormalisationV1))) + Expect(baseCd.Signatures[0].Digest.HashAlgorithm).To(BeIdenticalTo("sha256")) Expect(baseCd.Signatures[0].Digest.Value).To(BeIdenticalTo(correctBaseCdHash)) Expect(baseCd.Signatures[0].Signature.Algorithm).To(BeIdenticalTo("testSignAlgorithm")) Expect(baseCd.Signatures[0].Signature.Value).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", "sha256", correctBaseCdHash))) @@ -125,8 +127,9 @@ var _ = Describe("Sign/Verify component-descriptor", func() { baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ Name: "testSignAlgorithmNOTRight", Digest: v2.DigestSpec{ - Algorithm: "testAlgorithm", - Value: "testValue", + HashAlgorithm: "testAlgorithm", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: "testValue", }, Signature: v2.SignatureSpec{ Algorithm: "testSigning", diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index acd2ac3e..68b60741 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "encoding/hex" "fmt" v2 "github.com/gardener/component-spec/bindings-go/apis/v2" @@ -28,8 +27,9 @@ func main() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: "00000000000000", }, }, }, @@ -43,8 +43,9 @@ func main() { }, }, Digest: &v2.DigestSpec{ - Algorithm: "sha256", - Value: "00000000000000", + HashAlgorithm: "sha256", + NormalisationAlgorithm: string(v2.ManifestDigestV1), + Value: "00000000000000", }, }, }, @@ -53,13 +54,15 @@ func main() { ctx := context.TODO() err := signatures.AddDigestsToComponentDescriptor(ctx, &cd, func(ctx context.Context, cd v2.ComponentDescriptor, cr v2.ComponentReference) (*v2.DigestSpec, error) { return &v2.DigestSpec{ - Algorithm: "testing", - Value: string(cr.GetIdentityDigest()), + HashAlgorithm: "testing", + NormalisationAlgorithm: string(v2.JsonNormalisationV1), + Value: string(cr.GetIdentityDigest()), }, nil }, func(ctx context.Context, cd v2.ComponentDescriptor, r v2.Resource) (*v2.DigestSpec, error) { return &v2.DigestSpec{ - Algorithm: "testing", - Value: string(r.GetIdentityDigest()), + HashAlgorithm: "testing", + NormalisationAlgorithm: string(v2.ManifestDigestV1), + Value: string(r.GetIdentityDigest()), }, nil }) if err != nil { @@ -71,12 +74,12 @@ func main() { fmt.Printf("ERROR: %s", err) } - norm, err := signatures.HashForComponentDescriptor(cd, hasher.HashFunction) + norm, err := signatures.HashForComponentDescriptor(cd, *hasher) if err != nil { fmt.Printf("ERROR: %s", err) return } - fmt.Println(hex.EncodeToString(norm)) + fmt.Println(norm.Value) signer, err := signatures.CreateRsaSignerFromKeyFile("private") if err != nil { From 1b370ec4c6facec373b3fea5ba4d4dd889fb6f9c Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 20 Jan 2022 18:01:10 +0100 Subject: [PATCH 20/36] improved normalisation better types, disable html escaping --- bindings-go/apis/v2/signatures/normalize.go | 19 +++++++++++++++---- bindings-go/examples/signatures/main.go | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 265e74fe..17da0197 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -1,6 +1,7 @@ package signatures import ( + "bytes" "context" "encoding/hex" "encoding/json" @@ -67,7 +68,7 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { {"schemaVersion": cd.Metadata.Version}, } - componentReferences := [][]Entry{} + componentReferences := []interface{}{} for _, ref := range cd.ComponentSpec.ComponentReferences { extraIdentity := buildExtraIdentity(ref.ExtraIdentity) @@ -86,7 +87,7 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { componentReferences = append(componentReferences, componentReference) } - resources := [][]Entry{} + resources := []interface{}{} for _, res := range cd.ComponentSpec.Resources { extraIdentity := buildExtraIdentity(res.ExtraIdentity) @@ -121,12 +122,22 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { return nil, fmt.Errorf("failed sorting during normalisation: %w", err) } - normalizedJson, err := json.Marshal(normalizedComponentDescriptor) + byteBuffer := bytes.NewBuffer([]byte{}) + encoder := json.NewEncoder(byteBuffer) + encoder.SetEscapeHTML(false) + err := encoder.Encode(normalizedComponentDescriptor) if err != nil { return nil, err } + normalizedJson := byteBuffer.Bytes() + + // encoder.Encode appends a newline that we do not want + if normalizedJson[len(normalizedJson)-1] == 10 { + normalizedJson = normalizedJson[:len(normalizedJson)-1] + } + return normalizedJson, nil } @@ -158,7 +169,7 @@ func deepSort(in interface{}) error { if err := deepSort(val); err != nil { return err } - case [][]Entry: + case []interface{}: for _, v := range castIn { if err := deepSort(v); err != nil { return err diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index 68b60741..770863b0 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -15,7 +15,7 @@ func main() { }, ComponentSpec: v2.ComponentSpec{ ObjectMeta: v2.ObjectMeta{ - Name: "CD-Name", + Name: "CD-Namecool Unicode ♥ unprintable characters \u0007 \u0031", Version: "v0.0.1", }, ComponentReferences: []v2.ComponentReference{ From 1c6ec8cbb13a32e5e30e312b6ebab269d395e82f Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Mon, 24 Jan 2022 17:37:43 +0100 Subject: [PATCH 21/36] Add Resource Digster interface and localOciBlobV1 type --- bindings-go/apis/v2/componentdescriptor.go | 5 +++-- bindings-go/apis/v2/signatures/types.go | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index e2c7877a..3959dcd9 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -459,8 +459,9 @@ type SignatureSpec struct { type NormalisationAlgorithm string const ( - JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisationV1" - ManifestDigestV1 NormalisationAlgorithm = "manifestDigestV1" + JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisationV1" + ManifestDigestV1 NormalisationAlgorithm = "manifestDigestV1" + LocalOciBlobDigestV1 NormalisationAlgorithm = "localOciBlobDigestV1" ) // Signature defines a digest and corresponding signature, identifyable by name. diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index 0c16156e..f41a3d3b 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -1,6 +1,7 @@ package signatures import ( + "context" "crypto/sha256" "fmt" "hash" @@ -40,3 +41,7 @@ func HasherForName(algorithmName string) (*Hasher, error) { } return nil, fmt.Errorf("hash algorithm %s not found/implemented", algorithmName) } + +type ResourceDigester interface { + DigestForResource(ctx context.Context, componentDescriptor v2.ComponentDescriptor, resource v2.Resource, hasher Hasher) (*v2.DigestSpec, error) +} From 5fa1b739d30fda9d2e836f81a1cfc84f0a80e58c Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Mon, 14 Feb 2022 16:54:14 +0100 Subject: [PATCH 22/36] ignore digest for res.access.type == None --- bindings-go/apis/v2/signatures/normalize.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 17da0197..809ab200 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -91,6 +91,17 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { for _, res := range cd.ComponentSpec.Resources { extraIdentity := buildExtraIdentity(res.ExtraIdentity) + //ignore access.type=None for normalisation and hash calculation + if res.Access.Type == "None" { + resource := []Entry{ + {"name": res.Name}, + {"version": res.Version}, + {"extraIdentity": extraIdentity}, + } + resources = append(resources, resource) + continue + } + digest := []Entry{ {"hashAlgorithm": res.Digest.HashAlgorithm}, {"normalisationAlgorithm": res.Digest.NormalisationAlgorithm}, @@ -211,6 +222,10 @@ func isNormaliseable(cd v2.ComponentDescriptor) error { // check for digests on resources for _, res := range cd.Resources { + //ignore access.type=None for normalisation and hash calculation + if res.Access.Type == "None" { + continue + } if res.Digest == nil || res.Digest.HashAlgorithm == "" || res.Digest.NormalisationAlgorithm == "" || res.Digest.Value == "" { return fmt.Errorf("missing digest in resource for %s:%s", res.Name, res.Version) } From aa4954db5376943e7ce3e8a45c78782f5ae444a4 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Mon, 14 Feb 2022 16:54:45 +0100 Subject: [PATCH 23/36] add s3 types --- bindings-go/apis/v2/accesstypes.go | 28 ++++++++++++++++++++++ bindings-go/apis/v2/componentdescriptor.go | 1 + 2 files changed, 29 insertions(+) diff --git a/bindings-go/apis/v2/accesstypes.go b/bindings-go/apis/v2/accesstypes.go index cd34d0be..9153f0a6 100644 --- a/bindings-go/apis/v2/accesstypes.go +++ b/bindings-go/apis/v2/accesstypes.go @@ -219,3 +219,31 @@ func NewGitHubAccess(url, ref, commit string) *GitHubAccess { func (a GitHubAccess) GetType() string { return GitHubAccessType } + +// S3AccessType is the type of a s3 acces. +const S3AccessType = "s3" + +// S3AccessType describes a github repository resource access. +type S3Access struct { + ObjectType `json:",inline"` + + // BucketName is the name of the s3 bucket. + BucketName string `json:"bucketName"` + // ObjectKey describes the referenced object. + ObjectKey string `json:"objectKey"` +} + +// NewGitHubAccess creates a new Web accessor +func NewS3Access(bucketName, objectKey string) *S3Access { + return &S3Access{ + ObjectType: ObjectType{ + Type: S3AccessType, + }, + BucketName: bucketName, + ObjectKey: objectKey, + } +} + +func (a S3Access) GetType() string { + return S3AccessType +} diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 3959dcd9..642c052b 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -462,6 +462,7 @@ const ( JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisationV1" ManifestDigestV1 NormalisationAlgorithm = "manifestDigestV1" LocalOciBlobDigestV1 NormalisationAlgorithm = "localOciBlobDigestV1" + GenericBlobDigestV1 NormalisationAlgorithm = "genericBlobDigestV1" ) // Signature defines a digest and corresponding signature, identifyable by name. From f31d5ff93c0f6ed8131aba2372c278d17db264b9 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Mon, 14 Feb 2022 17:01:11 +0100 Subject: [PATCH 24/36] allow missing res digests in normailsation --- bindings-go/apis/v2/signatures/normalize.go | 25 ++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 809ab200..5d9b3656 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -60,7 +60,7 @@ func HashForComponentDescriptor(cd v2.ComponentDescriptor, hash Hasher) (*v2.Dig } func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { - if err := isNormaliseable(cd); err != nil { + if err := isNormaliseableUnsafe(cd); err != nil { return nil, fmt.Errorf("can not normalise component-descriptor %s:%s: %w", cd.Name, cd.Version, err) } @@ -102,6 +102,17 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { continue } + //ignore a resource without digests + if res.Digest == nil { + resource := []Entry{ + {"name": res.Name}, + {"version": res.Version}, + {"extraIdentity": extraIdentity}, + } + resources = append(resources, resource) + continue + } + digest := []Entry{ {"hashAlgorithm": res.Digest.HashAlgorithm}, {"normalisationAlgorithm": res.Digest.NormalisationAlgorithm}, @@ -232,3 +243,15 @@ func isNormaliseable(cd v2.ComponentDescriptor) error { } return nil } + +// isNormaliseableUnsafe checks if componentReferences contain digest. It does not check resources for containing digests. +// Does NOT verify if the digests are correct +func isNormaliseableUnsafe(cd v2.ComponentDescriptor) error { + // check for digests on component references + for _, reference := range cd.ComponentReferences { + if reference.Digest == nil || reference.Digest.HashAlgorithm == "" || reference.Digest.NormalisationAlgorithm == "" || reference.Digest.Value == "" { + return fmt.Errorf("missing digest in componentReference for %s:%s", reference.Name, reference.Version) + } + } + return nil +} From 7b74c2ab6fba7aafc4be5f86d9d6543cee2ac469 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 22 Feb 2022 15:50:05 +0100 Subject: [PATCH 25/36] hash algorithm to lowercase --- bindings-go/apis/v2/signatures/rsa.go | 4 ++-- bindings-go/apis/v2/signatures/types.go | 6 +++--- bindings-go/examples/signatures/main.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index 917be1fd..2b838505 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -61,8 +61,8 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.Di // maps a hashing algorithm string to crypto.Hash func hashAlgorithmLookup(algorithm string) (crypto.Hash, error) { - switch strings.ToUpper(algorithm) { - case "SHA256": + switch strings.ToLower(algorithm) { + case "sha256": return crypto.SHA256, nil } return 0, fmt.Errorf("hash Algorithm %s not found", algorithm) diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index f41a3d3b..fae57ba7 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -32,11 +32,11 @@ type Hasher struct { // HasherForName creates a Hasher instance for the algorithmName. func HasherForName(algorithmName string) (*Hasher, error) { - switch strings.ToUpper(algorithmName) { - case "SHA256": + switch strings.ToLower(algorithmName) { + case "sha256": return &Hasher{ HashFunction: sha256.New(), - AlgorithmName: "SHA256", + AlgorithmName: "sha256", }, nil } return nil, fmt.Errorf("hash algorithm %s not found/implemented", algorithmName) diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index 770863b0..4e3a85f8 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -69,7 +69,7 @@ func main() { fmt.Printf("ERROR addingDigestsToComponentDescriptor %s", err) } - hasher, err := signatures.HasherForName("SHA256") + hasher, err := signatures.HasherForName("sha256") if err != nil { fmt.Printf("ERROR: %s", err) } From 5cce38caa49a692dede03b0564195333a529c941 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 22 Feb 2022 16:13:12 +0100 Subject: [PATCH 26/36] update tests to current logic --- bindings-go/apis/v2/signatures/normalize.go | 2 +- bindings-go/apis/v2/signatures/normalize_test.go | 13 ++----------- bindings-go/apis/v2/signatures/sign_test.go | 1 + 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 5d9b3656..78a182e6 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -92,7 +92,7 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { extraIdentity := buildExtraIdentity(res.ExtraIdentity) //ignore access.type=None for normalisation and hash calculation - if res.Access.Type == "None" { + if res.Access == nil || res.Access.Type == "None" { resource := []Entry{ {"name": res.Name}, {"version": res.Version}, diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index a8633aa6..f571a357 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -52,6 +52,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { NormalisationAlgorithm: string(v2.ManifestDigestV1), Value: "00000000000000", }, + Access: v2.NewUnstructuredType(v2.OCIRegistryType, map[string]interface{}{"imageRef": "ref"}), }, }, }, @@ -68,17 +69,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Expect(err).ToNot(BeNil()) }) }) - Describe("missing resource Digest", func() { - It("should fail to hash", func() { - baseCd.ComponentSpec.Resources[0].Digest = nil - hasher, err := signatures.HasherForName("sha256") - Expect(err).To(BeNil()) - hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) - Expect(hash).To(BeNil()) - Expect(err).ToNot(BeNil()) - }) - }) - Describe("should give the correct hash hash", func() { + Describe("should give the correct hash", func() { It("with sha256", func() { hasher, err := signatures.HasherForName("sha256") Expect(err).To(BeNil()) diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index dc985071..acb247f7 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -79,6 +79,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { NormalisationAlgorithm: string(v2.ManifestDigestV1), Value: "00000000000000", }, + Access: v2.NewUnstructuredType(v2.OCIRegistryType, map[string]interface{}{"imageRef": "ref"}), }, }, }, From 8f95d2a5e13bc38ecb64446415f15becd5991640 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Tue, 22 Feb 2022 16:30:30 +0100 Subject: [PATCH 27/36] changed naming of normalisation algorithms --- bindings-go/apis/v2/componentdescriptor.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 642c052b..92784406 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -459,10 +459,10 @@ type SignatureSpec struct { type NormalisationAlgorithm string const ( - JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisationV1" - ManifestDigestV1 NormalisationAlgorithm = "manifestDigestV1" - LocalOciBlobDigestV1 NormalisationAlgorithm = "localOciBlobDigestV1" - GenericBlobDigestV1 NormalisationAlgorithm = "genericBlobDigestV1" + JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisation/V1" + ManifestDigestV1 NormalisationAlgorithm = "manifestDigest/V1" + LocalOciBlobDigestV1 NormalisationAlgorithm = "localOciBlobDigest/V1" + GenericBlobDigestV1 NormalisationAlgorithm = "genericBlobDigest/V1" ) // Signature defines a digest and corresponding signature, identifyable by name. From ee178992cbdef0e1879beb5642c165ea0ef9d818 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Wed, 23 Feb 2022 16:21:23 +0100 Subject: [PATCH 28/36] addapt python and json schema to new signature properties --- bindings-python/gci/componentmodel.py | 20 +++++++- .../component-descriptor-v2-schema.yaml | 47 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/bindings-python/gci/componentmodel.py b/bindings-python/gci/componentmodel.py index d789b8e8..2c9f4425 100644 --- a/bindings-python/gci/componentmodel.py +++ b/bindings-python/gci/componentmodel.py @@ -171,6 +171,22 @@ def set_label( labels=patched_labels, ) +@dc +class DigestSpec: + hashAlgorithm: str + normalisationAlgorithm: str + value: str + +@dc +class SignatureSpec: + algorithm: str + value: str + +@dc +class Signature: + name: str + digest: DigestSpec + signature: SignatureSpec class Provider(enum.Enum): ''' @@ -311,6 +327,7 @@ class ComponentReference(Artifact, LabelMethodsMixin): name: str componentName: str version: str + digest: DigestSpec extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) labels: typing.List[Label] = dataclasses.field(default_factory=tuple) @@ -339,12 +356,12 @@ class Resource(Artifact, LabelMethodsMixin): ResourceAccess, None, ] + digest: DigestSpec extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) relation: ResourceRelation = ResourceRelation.LOCAL labels: typing.List[Label] = dataclasses.field(default_factory=tuple) srcRefs: typing.List[SourceReference] = dataclasses.field(default_factory=tuple) - @dc(frozen=True) class RepositoryContext: pass # actually, must have attr `type` @@ -412,6 +429,7 @@ def enum_or_string(v, enum_type: enum.Enum): class ComponentDescriptor: meta: Metadata component: Component + signatures: typing.List[Signature] @staticmethod def validate( diff --git a/language-independent/component-descriptor-v2-schema.yaml b/language-independent/component-descriptor-v2-schema.yaml index 6fa69a8d..dade5732 100644 --- a/language-independent/component-descriptor-v2-schema.yaml +++ b/language-independent/component-descriptor-v2-schema.yaml @@ -128,6 +128,45 @@ definitions: - $ref: '#/definitions/githubAccess' - $ref: '#/definitions/httpAccess' + digestSpec: + type: 'object' + required: + - hashAlgorithm + - normalisationAlgorithm + - value + properties: + hashAlgorithm: + type: string + normalisationAlgorithm: + type: string + value: + type: string + + signatureSpec: + type: 'object' + required: + - algorithm + - value + properties: + algorithm: + type: string + value: + type: string + + signature: + type: 'object' + required: + - name + - digest + - signature + properties: + name: + type: string + digest: + $ref: '#/definitions/digestSpec' + signature: + $ref: '#/definitions/signatureSpec' + srcRef: type: 'object' description: 'a reference to a (component-local) source' @@ -160,6 +199,8 @@ definitions: type: 'array' items: $ref: '#/definitions/label' + digest: + $ref: '#/definitions/digestSpec' resourceType: type: 'object' @@ -197,6 +238,8 @@ definitions: - $ref: '#/definitions/ociBlobAccess' - $ref: '#/definitions/localFilesystemBlobAccess' - $ref: '#/definitions/localOciBlobAccess' + digest: + $ref: '#/definitions/digestSpec' ociImageAccess: type: 'object' @@ -385,3 +428,7 @@ properties: $ref: '#/definitions/meta' component: $ref: '#/definitions/component' + signatures: + type: 'array' + items: + $ref: '#/definitions/signature' From ef3ab49361548c3f1ffc8f1de89529529cc41534 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 24 Feb 2022 14:49:35 +0100 Subject: [PATCH 29/36] fix updated python and json schema adoption --- bindings-python/gci/componentmodel.py | 6 +++--- .../component-descriptor-v2-schema.yaml | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bindings-python/gci/componentmodel.py b/bindings-python/gci/componentmodel.py index 2c9f4425..d9cc6495 100644 --- a/bindings-python/gci/componentmodel.py +++ b/bindings-python/gci/componentmodel.py @@ -327,7 +327,7 @@ class ComponentReference(Artifact, LabelMethodsMixin): name: str componentName: str version: str - digest: DigestSpec + digest: typing.Optional[DigestSpec] = dataclasses.field(default=None) extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) labels: typing.List[Label] = dataclasses.field(default_factory=tuple) @@ -356,7 +356,7 @@ class Resource(Artifact, LabelMethodsMixin): ResourceAccess, None, ] - digest: DigestSpec + digest: typing.Optional[DigestSpec] = dataclasses.field(default=None) extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) relation: ResourceRelation = ResourceRelation.LOCAL labels: typing.List[Label] = dataclasses.field(default_factory=tuple) @@ -429,7 +429,7 @@ def enum_or_string(v, enum_type: enum.Enum): class ComponentDescriptor: meta: Metadata component: Component - signatures: typing.List[Signature] + signatures: typing.List[Signature] = dataclasses.field(default_factory=list) @staticmethod def validate( diff --git a/language-independent/component-descriptor-v2-schema.yaml b/language-independent/component-descriptor-v2-schema.yaml index dade5732..008ecf33 100644 --- a/language-independent/component-descriptor-v2-schema.yaml +++ b/language-independent/component-descriptor-v2-schema.yaml @@ -200,7 +200,9 @@ definitions: items: $ref: '#/definitions/label' digest: - $ref: '#/definitions/digestSpec' + oneOf: + - type: 'null' + - $ref: '#/definitions/digestSpec' resourceType: type: 'object' @@ -239,7 +241,9 @@ definitions: - $ref: '#/definitions/localFilesystemBlobAccess' - $ref: '#/definitions/localOciBlobAccess' digest: - $ref: '#/definitions/digestSpec' + oneOf: + - type: 'null' + - $ref: '#/definitions/digestSpec' ociImageAccess: type: 'object' @@ -325,6 +329,11 @@ definitions: $ref: '#/definitions/label' access: $ref: '#/definitions/ociImageAccess' + digest: + oneOf: + - type: 'null' + - $ref: '#/definitions/digestSpec' + httpAccess: type: 'object' @@ -371,6 +380,10 @@ definitions: $ref: '#/definitions/label' access: $ref: '#/definitions/genericAccess' + digest: + oneOf: + - type: 'null' + - $ref: '#/definitions/digestSpec' component: type: 'object' From 8137ce2189a9e2eb2046d903e6ad77bd7586027c Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Thu, 24 Feb 2022 15:37:29 +0100 Subject: [PATCH 30/36] python improvement --- bindings-python/gci/componentmodel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings-python/gci/componentmodel.py b/bindings-python/gci/componentmodel.py index d9cc6495..21e11f6e 100644 --- a/bindings-python/gci/componentmodel.py +++ b/bindings-python/gci/componentmodel.py @@ -327,7 +327,7 @@ class ComponentReference(Artifact, LabelMethodsMixin): name: str componentName: str version: str - digest: typing.Optional[DigestSpec] = dataclasses.field(default=None) + digest: typing.Optional[DigestSpec] = None extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) labels: typing.List[Label] = dataclasses.field(default_factory=tuple) @@ -356,7 +356,7 @@ class Resource(Artifact, LabelMethodsMixin): ResourceAccess, None, ] - digest: typing.Optional[DigestSpec] = dataclasses.field(default=None) + digest: typing.Optional[DigestSpec] = None extraIdentity: typing.Dict[str, str] = dataclasses.field(default_factory=dict) relation: ResourceRelation = ResourceRelation.LOCAL labels: typing.List[Label] = dataclasses.field(default_factory=tuple) From 4f8daa0f4a5deb38d999b80b02c73bca204d34dc Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 10:39:50 +0100 Subject: [PATCH 31/36] adapt tests to changed normalisation type --- bindings-go/apis/v2/signatures/normalize_test.go | 4 ++-- bindings-go/apis/v2/signatures/sign_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index f571a357..bc13861c 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -10,9 +10,9 @@ import ( var _ = Describe("Normalise/Hash component-descriptor", func() { var baseCd v2.ComponentDescriptor - correctBaseCdHash := "64c04405dcd03a6f345584adb860cad4f4ed6dba1943d5535db3407b2bf9f000" + correctBaseCdHash := "5995a530e81af5e974fe377f1079991c4e6a762bcff2cb92334f80d3a1da5a8a" //corresponding normalised CD: - //[{"component":[{"componentReferences":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"jsonNormalisationV1"},{"value":"00000000000000"}]},{"extraIdentity":[{"refKey":"refName"}]},{"name":"compRefName"},{"version":"v0.0.2compRef"}]]},{"name":"CD-Name"},{"resources":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"manifestDigestV1"},{"value":"00000000000000"}]},{"extraIdentity":[{"key":"value"}]},{"name":"Resource1"},{"version":"v0.0.3resource"}]]},{"version":"v0.0.1"}]},{"meta":[{"schemaVersion":"v2"}]}] + //[{"component":[{"componentReferences":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"jsonNormalisation/V1"},{"value":"00000000000000"}]},{"extraIdentity":[{"refKey":"refName"}]},{"name":"compRefName"},{"version":"v0.0.2compRef"}]]},{"name":"CD-Name"},{"resources":[[{"digest":[{"hashAlgorithm":"sha256"},{"normalisationAlgorithm":"manifestDigest/V1"},{"value":"00000000000000"}]},{"extraIdentity":[{"key":"value"}]},{"name":"Resource1"},{"version":"v0.0.3resource"}]]},{"version":"v0.0.1"}]},{"meta":[{"schemaVersion":"v2"}]}] BeforeEach(func() { baseCd = v2.ComponentDescriptor{ Metadata: v2.Metadata{ diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index acb247f7..52af2662 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -38,7 +38,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { AlgorithmName: "sha256", } signatureName := "testSignatureName" - correctBaseCdHash := "64c04405dcd03a6f345584adb860cad4f4ed6dba1943d5535db3407b2bf9f000" + correctBaseCdHash := "5995a530e81af5e974fe377f1079991c4e6a762bcff2cb92334f80d3a1da5a8a" BeforeEach(func() { baseCd = v2.ComponentDescriptor{ From 20aeac9c998d434e3f524ba3f8128a0ec13aa117 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 10:44:02 +0100 Subject: [PATCH 32/36] removed unused code --- bindings-go/apis/v2/componentdescriptor.go | 7 +++---- bindings-go/apis/v2/signatures/normalize.go | 23 --------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/bindings-go/apis/v2/componentdescriptor.go b/bindings-go/apis/v2/componentdescriptor.go index 92784406..42b40cac 100644 --- a/bindings-go/apis/v2/componentdescriptor.go +++ b/bindings-go/apis/v2/componentdescriptor.go @@ -459,10 +459,9 @@ type SignatureSpec struct { type NormalisationAlgorithm string const ( - JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisation/V1" - ManifestDigestV1 NormalisationAlgorithm = "manifestDigest/V1" - LocalOciBlobDigestV1 NormalisationAlgorithm = "localOciBlobDigest/V1" - GenericBlobDigestV1 NormalisationAlgorithm = "genericBlobDigest/V1" + JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisation/V1" + ManifestDigestV1 NormalisationAlgorithm = "manifestDigest/V1" + GenericBlobDigestV1 NormalisationAlgorithm = "genericBlobDigest/V1" ) // Signature defines a digest and corresponding signature, identifyable by name. diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 78a182e6..473d4fe8 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -221,29 +221,6 @@ func getOnlyValueInEntry(entry Entry) interface{} { return value } -// isNormaliseable checks if componentReferences and resources contain digest -// Does NOT verify if the digests are correct -func isNormaliseable(cd v2.ComponentDescriptor) error { - // check for digests on component references - for _, reference := range cd.ComponentReferences { - if reference.Digest == nil || reference.Digest.HashAlgorithm == "" || reference.Digest.NormalisationAlgorithm == "" || reference.Digest.Value == "" { - return fmt.Errorf("missing digest in componentReference for %s:%s", reference.Name, reference.Version) - } - } - - // check for digests on resources - for _, res := range cd.Resources { - //ignore access.type=None for normalisation and hash calculation - if res.Access.Type == "None" { - continue - } - if res.Digest == nil || res.Digest.HashAlgorithm == "" || res.Digest.NormalisationAlgorithm == "" || res.Digest.Value == "" { - return fmt.Errorf("missing digest in resource for %s:%s", res.Name, res.Version) - } - } - return nil -} - // isNormaliseableUnsafe checks if componentReferences contain digest. It does not check resources for containing digests. // Does NOT verify if the digests are correct func isNormaliseableUnsafe(cd v2.ComponentDescriptor) error { From 6ebc5b95a7b86cb43ac6db6abe29448bd1928943 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 10:46:59 +0100 Subject: [PATCH 33/36] docstring --- bindings-go/apis/v2/accesstypes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings-go/apis/v2/accesstypes.go b/bindings-go/apis/v2/accesstypes.go index 9153f0a6..fa27dbb5 100644 --- a/bindings-go/apis/v2/accesstypes.go +++ b/bindings-go/apis/v2/accesstypes.go @@ -220,10 +220,10 @@ func (a GitHubAccess) GetType() string { return GitHubAccessType } -// S3AccessType is the type of a s3 acces. +// S3AccessType is the type of a s3 access. const S3AccessType = "s3" -// S3AccessType describes a github repository resource access. +// S3AccessType describes a s3 resource access. type S3Access struct { ObjectType `json:",inline"` @@ -233,7 +233,7 @@ type S3Access struct { ObjectKey string `json:"objectKey"` } -// NewGitHubAccess creates a new Web accessor +// NewS3Access creates a new s3 accessor func NewS3Access(bucketName, objectKey string) *S3Access { return &S3Access{ ObjectType: ObjectType{ From 5206bc9f6881f00a21cd1bf8dba4619f9890536e Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 11:00:28 +0100 Subject: [PATCH 34/36] error handlingimprovement --- bindings-go/apis/v2/signatures/normalize.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize.go b/bindings-go/apis/v2/signatures/normalize.go index 473d4fe8..aa10eaae 100644 --- a/bindings-go/apis/v2/signatures/normalize.go +++ b/bindings-go/apis/v2/signatures/normalize.go @@ -148,8 +148,7 @@ func normalizeComponentDescriptor(cd v2.ComponentDescriptor) ([]byte, error) { encoder := json.NewEncoder(byteBuffer) encoder.SetEscapeHTML(false) - err := encoder.Encode(normalizedComponentDescriptor) - if err != nil { + if err := encoder.Encode(normalizedComponentDescriptor); err != nil { return nil, err } From e0968b5dd253a77f9dcea3d2bf352d744fdb8e29 Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 11:33:25 +0100 Subject: [PATCH 35/36] typos --- bindings-go/apis/v2/signatures/normalize_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index bc13861c..cf997b4f 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -79,7 +79,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }) }) Describe("should ignore modifications in unhashed fields", func() { - It("should succed with signature changes", func() { + It("should succeed with signature changes", func() { baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ Name: "TestSig", Digest: v2.DigestSpec{ @@ -98,7 +98,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Expect(err).To(BeNil()) Expect(hash.Value).To(Equal(correctBaseCdHash)) }) - It("should succed with source changes", func() { + It("should succeed with source changes", func() { baseCd.Sources = append(baseCd.Sources, v2.Source{ IdentityObjectMeta: v2.IdentityObjectMeta{ Name: "source1", @@ -111,7 +111,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Expect(err).To(BeNil()) Expect(hash.Value).To(Equal(correctBaseCdHash)) }) - It("should succed with resource access reference changes", func() { + It("should succeed with resource access reference changes", func() { access, err := v2.NewUnstructured(v2.NewOCIRegistryAccess("ociRef/path/to/image")) Expect(err).To(BeNil()) baseCd.Resources[0].Access = &access From d3ec35e65639af1a244c61874583bc746794da1d Mon Sep 17 00:00:00 2001 From: enrico-kaack-comp Date: Fri, 25 Feb 2022 11:55:45 +0100 Subject: [PATCH 36/36] introduced const for hash algorithm --- bindings-go/apis/v2/signatures/normalize_test.go | 16 ++++++++-------- bindings-go/apis/v2/signatures/rsa.go | 2 +- bindings-go/apis/v2/signatures/rsa_test.go | 6 +++--- bindings-go/apis/v2/signatures/sign_test.go | 10 +++++----- bindings-go/apis/v2/signatures/types.go | 6 ++++-- bindings-go/examples/signatures/main.go | 6 +++--- 6 files changed, 24 insertions(+), 22 deletions(-) diff --git a/bindings-go/apis/v2/signatures/normalize_test.go b/bindings-go/apis/v2/signatures/normalize_test.go index cf997b4f..053230b7 100644 --- a/bindings-go/apis/v2/signatures/normalize_test.go +++ b/bindings-go/apis/v2/signatures/normalize_test.go @@ -32,7 +32,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: "00000000000000", }, @@ -48,7 +48,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }, }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.ManifestDigestV1), Value: "00000000000000", }, @@ -62,7 +62,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Describe("missing componentReference Digest", func() { It("should fail to hash", func() { baseCd.ComponentSpec.ComponentReferences[0].Digest = nil - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) Expect(err).To(BeNil()) hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(hash).To(BeNil()) @@ -71,7 +71,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { }) Describe("should give the correct hash", func() { It("with sha256", func() { - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) Expect(err).To(BeNil()) hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) @@ -83,7 +83,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { baseCd.Signatures = append(baseCd.Signatures, v2.Signature{ Name: "TestSig", Digest: v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: "00000", }, @@ -92,7 +92,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Value: "0000", }, }) - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) Expect(err).To(BeNil()) hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) @@ -105,7 +105,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { Version: "v0.0.0", }, }) - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) Expect(err).To(BeNil()) hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) @@ -115,7 +115,7 @@ var _ = Describe("Normalise/Hash component-descriptor", func() { access, err := v2.NewUnstructured(v2.NewOCIRegistryAccess("ociRef/path/to/image")) Expect(err).To(BeNil()) baseCd.Resources[0].Access = &access - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) Expect(err).To(BeNil()) hash, err := signatures.HashForComponentDescriptor(baseCd, *hasher) Expect(err).To(BeNil()) diff --git a/bindings-go/apis/v2/signatures/rsa.go b/bindings-go/apis/v2/signatures/rsa.go index 2b838505..4315ef94 100644 --- a/bindings-go/apis/v2/signatures/rsa.go +++ b/bindings-go/apis/v2/signatures/rsa.go @@ -62,7 +62,7 @@ func (s RsaSigner) Sign(componentDescriptor v2.ComponentDescriptor, digest v2.Di // maps a hashing algorithm string to crypto.Hash func hashAlgorithmLookup(algorithm string) (crypto.Hash, error) { switch strings.ToLower(algorithm) { - case "sha256": + case SHA256: return crypto.SHA256, nil } return 0, fmt.Errorf("hash Algorithm %s not found", algorithm) diff --git a/bindings-go/apis/v2/signatures/rsa_test.go b/bindings-go/apis/v2/signatures/rsa_test.go index 5143086f..53617555 100644 --- a/bindings-go/apis/v2/signatures/rsa_test.go +++ b/bindings-go/apis/v2/signatures/rsa_test.go @@ -52,7 +52,7 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) Expect(err).To(BeNil()) signature, err := signer.Sign(v2.ComponentDescriptor{}, v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: hex.EncodeToString(hashOfString[:]), }) @@ -82,7 +82,7 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathPrivateKey) Expect(err).To(BeNil()) digest := v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: hex.EncodeToString(hashOfString[:]), } @@ -111,7 +111,7 @@ var _ = Describe("RSA Sign/Verify", func() { signer, err := signatures.CreateRsaSignerFromKeyFile(pathWrongPrivateKey) Expect(err).To(BeNil()) digest := v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: hex.EncodeToString(hashOfString[:]), } diff --git a/bindings-go/apis/v2/signatures/sign_test.go b/bindings-go/apis/v2/signatures/sign_test.go index 52af2662..6e3e475e 100644 --- a/bindings-go/apis/v2/signatures/sign_test.go +++ b/bindings-go/apis/v2/signatures/sign_test.go @@ -35,7 +35,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { var baseCd v2.ComponentDescriptor testSHA256Hasher := signatures.Hasher{ HashFunction: sha256.New(), - AlgorithmName: "sha256", + AlgorithmName: signatures.SHA256, } signatureName := "testSignatureName" correctBaseCdHash := "5995a530e81af5e974fe377f1079991c4e6a762bcff2cb92334f80d3a1da5a8a" @@ -59,7 +59,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: "00000000000000", }, @@ -75,7 +75,7 @@ var _ = Describe("Sign/Verify component-descriptor", func() { }, }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.ManifestDigestV1), Value: "00000000000000", }, @@ -93,10 +93,10 @@ var _ = Describe("Sign/Verify component-descriptor", func() { Expect(len(baseCd.Signatures)).To(BeIdenticalTo(1)) Expect(baseCd.Signatures[0].Name).To(BeIdenticalTo(signatureName)) Expect(baseCd.Signatures[0].Digest.NormalisationAlgorithm).To(BeIdenticalTo(string(v2.JsonNormalisationV1))) - Expect(baseCd.Signatures[0].Digest.HashAlgorithm).To(BeIdenticalTo("sha256")) + Expect(baseCd.Signatures[0].Digest.HashAlgorithm).To(BeIdenticalTo(signatures.SHA256)) Expect(baseCd.Signatures[0].Digest.Value).To(BeIdenticalTo(correctBaseCdHash)) Expect(baseCd.Signatures[0].Signature.Algorithm).To(BeIdenticalTo("testSignAlgorithm")) - Expect(baseCd.Signatures[0].Signature.Value).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", "sha256", correctBaseCdHash))) + Expect(baseCd.Signatures[0].Signature.Value).To(BeIdenticalTo(fmt.Sprintf("%s:%s-signed", signatures.SHA256, correctBaseCdHash))) }) }) Describe("verify component-descriptor signature", func() { diff --git a/bindings-go/apis/v2/signatures/types.go b/bindings-go/apis/v2/signatures/types.go index fae57ba7..e1ffdd3a 100644 --- a/bindings-go/apis/v2/signatures/types.go +++ b/bindings-go/apis/v2/signatures/types.go @@ -30,13 +30,15 @@ type Hasher struct { AlgorithmName string } +const SHA256 = "sha256" + // HasherForName creates a Hasher instance for the algorithmName. func HasherForName(algorithmName string) (*Hasher, error) { switch strings.ToLower(algorithmName) { - case "sha256": + case SHA256: return &Hasher{ HashFunction: sha256.New(), - AlgorithmName: "sha256", + AlgorithmName: SHA256, }, nil } return nil, fmt.Errorf("hash algorithm %s not found/implemented", algorithmName) diff --git a/bindings-go/examples/signatures/main.go b/bindings-go/examples/signatures/main.go index 4e3a85f8..0468e3ba 100644 --- a/bindings-go/examples/signatures/main.go +++ b/bindings-go/examples/signatures/main.go @@ -27,7 +27,7 @@ func main() { "refKey": "refName", }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.JsonNormalisationV1), Value: "00000000000000", }, @@ -43,7 +43,7 @@ func main() { }, }, Digest: &v2.DigestSpec{ - HashAlgorithm: "sha256", + HashAlgorithm: signatures.SHA256, NormalisationAlgorithm: string(v2.ManifestDigestV1), Value: "00000000000000", }, @@ -69,7 +69,7 @@ func main() { fmt.Printf("ERROR addingDigestsToComponentDescriptor %s", err) } - hasher, err := signatures.HasherForName("sha256") + hasher, err := signatures.HasherForName(signatures.SHA256) if err != nil { fmt.Printf("ERROR: %s", err) }