diff --git a/.github/config/golangci.yaml b/.github/config/golangci.yaml index 8352fbc2a3..01770b48d1 100644 --- a/.github/config/golangci.yaml +++ b/.github/config/golangci.yaml @@ -173,4 +173,26 @@ issues: - path: ignore/.*\.go linters: - dupword + # Deprecated algorithms and fields for extra identity field defaulting + # TODO: To be removed once v1 + v2 are removed. + - path: "cmds/.*|api/.*" + linters: + - staticcheck + text: "SA1019: jsonv1.Algorithm is deprecated" + - path: "cmds/.*|api/.*" + linters: + - staticcheck + text: "SA1019: compdesc.JsonNormalisationV1 is deprecated" + - path: "cmds/.*|api/.*" + linters: + - staticcheck + text: "SA1019: jsonv2.Algorithm is deprecated" + - path: "cmds/.*|api/.*" + linters: + - staticcheck + text: "SA1019: compdesc.JsonNormalisationV2 is deprecated" + - path: "cmds/.*|api/.*" + linters: + - staticcheck + text: "SA1019: legacy.DefaultingOfVersionIntoExtraIdentity is deprecated" diff --git a/.github/config/wordlist.txt b/.github/config/wordlist.txt index d38b0d4469..55c8a10bd4 100644 --- a/.github/config/wordlist.txt +++ b/.github/config/wordlist.txt @@ -308,4 +308,5 @@ xml yaml yitsushi yml -yyyy \ No newline at end of file +yyyy +jsonNormalisation \ No newline at end of file diff --git a/api/ocm/compdesc/norm_test.go b/api/ocm/compdesc/norm_test.go index 8d3991f076..786f35ea77 100644 --- a/api/ocm/compdesc/norm_test.go +++ b/api/ocm/compdesc/norm_test.go @@ -171,7 +171,7 @@ var _ = Describe("Normalization", func() { Expect(err).To(Succeed()) }) - It("hashes first", func() { + It("normalizes v1", func() { n, err := compdesc.Normalize(cd1, compdesc.JsonNormalisationV1) Expect(err).To(Succeed()) Expect(string(n)).To(StringEqualTrimmedWithContext("[{\"component\":[{\"componentReferences\":[]},{\"name\":\"github.com/vasu1124/introspect\"},{\"provider\":\"internal\"},{\"resources\":[[{\"digest\":[{\"hashAlgorithm\":\"SHA-256\"},{\"normalisationAlgorithm\":\"ociArtifactDigest/v1\"},{\"value\":\"6a1c7637a528ab5957ab60edf73b5298a0a03de02a96be0313ee89b22544840c\"}]},{\"extraIdentity\":null},{\"labels\":[[{\"name\":\"label2\"},{\"signing\":true},{\"value\":\"bar\"}]]},{\"name\":\"introspect-image\"},{\"relation\":\"local\"},{\"type\":\"ociImage\"},{\"version\":\"1.0.0\"}],[{\"digest\":[{\"hashAlgorithm\":\"SHA-256\"},{\"normalisationAlgorithm\":\"genericBlobDigest/v1\"},{\"value\":\"d1187ac17793b2f5fa26175c21cabb6ce388871ae989e16ff9a38bd6b32507bf\"}]},{\"extraIdentity\":null},{\"name\":\"introspect-blueprint\"},{\"relation\":\"local\"},{\"type\":\"landscaper.gardener.cloud/blueprint\"},{\"version\":\"1.0.0\"}],[{\"digest\":[{\"hashAlgorithm\":\"SHA-256\"},{\"normalisationAlgorithm\":\"ociArtifactDigest/v1\"},{\"value\":\"6229be2be7e328f74ba595d93b814b590b1aa262a1b85e49cc1492795a9e564c\"}]},{\"extraIdentity\":null},{\"name\":\"introspect-helm\"},{\"relation\":\"external\"},{\"type\":\"helm\"},{\"version\":\"0.1.0\"}]]},{\"version\":\"1.0.0\"}]},{\"meta\":[{\"schemaVersion\":\"v2\"}]}]")) @@ -180,13 +180,22 @@ var _ = Describe("Normalization", func() { Expect(o).To(Equal(n)) }) - It("hashes v2", func() { + It("normalizes v2", func() { n, err := compdesc.Normalize(cd1, compdesc.JsonNormalisationV2) Expect(err).To(Succeed()) Expect(string(n)).To(Equal(`{"component":{"componentReferences":[],"name":"github.com/vasu1124/introspect","provider":{"name":"internal"},"resources":[{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6a1c7637a528ab5957ab60edf73b5298a0a03de02a96be0313ee89b22544840c"},"labels":[{"name":"label2","signing":true,"value":"bar"}],"name":"introspect-image","relation":"local","type":"ociImage","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"genericBlobDigest/v1","value":"d1187ac17793b2f5fa26175c21cabb6ce388871ae989e16ff9a38bd6b32507bf"},"name":"introspect-blueprint","relation":"local","type":"landscaper.gardener.cloud/blueprint","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6229be2be7e328f74ba595d93b814b590b1aa262a1b85e49cc1492795a9e564c"},"name":"introspect-helm","relation":"external","type":"helm","version":"0.1.0"}],"sources":[{"name":"introspect","type":"git","version":"1.0.0"}],"version":"1.0.0"}}`)) }) - It("hashes v1 with none access", func() { + It("normalises v3", func() { + n, err := compdesc.Normalize(cd1, compdesc.JsonNormalisationV3) + Expect(err).To(Succeed()) + Expect(string(n)).To(Equal(`{"component":{"componentReferences":[],"name":"github.com/vasu1124/introspect","provider":{"name":"internal"},"resources":[{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6a1c7637a528ab5957ab60edf73b5298a0a03de02a96be0313ee89b22544840c"},"labels":[{"name":"label2","signing":true,"value":"bar"}],"name":"introspect-image","relation":"local","type":"ociImage","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"genericBlobDigest/v1","value":"d1187ac17793b2f5fa26175c21cabb6ce388871ae989e16ff9a38bd6b32507bf"},"name":"introspect-blueprint","relation":"local","type":"landscaper.gardener.cloud/blueprint","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6229be2be7e328f74ba595d93b814b590b1aa262a1b85e49cc1492795a9e564c"},"name":"introspect-helm","relation":"external","type":"helm","version":"0.1.0"}],"sources":[{"name":"introspect","type":"git","version":"1.0.0"}],"version":"1.0.0"}}`)) + o, err := compdesc.Normalize(cd1, compdesc.JsonNormalisationV2) + Expect(err).To(Succeed()) + Expect(o).To(Equal(n)) + }) + + It("normalizes v1 with none access", func() { cd1.Resources = append(cd1.Resources, compdesc.Resource{ ResourceMeta: compdesc.ResourceMeta{ ElementMeta: compdesc.ElementMeta{ @@ -208,7 +217,7 @@ var _ = Describe("Normalization", func() { Expect(string(n)).To(StringEqualWithContext(`[{"component":[{"componentReferences":[]},{"name":"github.com/vasu1124/introspect"},{"provider":"internal"},{"resources":[[{"digest":[{"hashAlgorithm":"SHA-256"},{"normalisationAlgorithm":"ociArtifactDigest/v1"},{"value":"6a1c7637a528ab5957ab60edf73b5298a0a03de02a96be0313ee89b22544840c"}]},{"extraIdentity":null},{"labels":[[{"name":"label2"},{"signing":true},{"value":"bar"}]]},{"name":"introspect-image"},{"relation":"local"},{"type":"ociImage"},{"version":"1.0.0"}],[{"digest":[{"hashAlgorithm":"SHA-256"},{"normalisationAlgorithm":"genericBlobDigest/v1"},{"value":"d1187ac17793b2f5fa26175c21cabb6ce388871ae989e16ff9a38bd6b32507bf"}]},{"extraIdentity":null},{"name":"introspect-blueprint"},{"relation":"local"},{"type":"landscaper.gardener.cloud/blueprint"},{"version":"1.0.0"}],[{"digest":[{"hashAlgorithm":"SHA-256"},{"normalisationAlgorithm":"ociArtifactDigest/v1"},{"value":"6229be2be7e328f74ba595d93b814b590b1aa262a1b85e49cc1492795a9e564c"}]},{"extraIdentity":null},{"name":"introspect-helm"},{"relation":"external"},{"type":"helm"},{"version":"0.1.0"}],[{"extraIdentity":null},{"name":"none"},{"relation":"local"},{"type":"plainText"},{"version":"v1"}]]},{"version":"1.0.0"}]},{"meta":[{"schemaVersion":"v2"}]}]`)) }) - It("hashes v2 with none access", func() { + It("normalizes v2 with none access", func() { cd1.Resources = append(cd1.Resources, compdesc.Resource{ ResourceMeta: compdesc.ResourceMeta{ ElementMeta: compdesc.ElementMeta{ @@ -230,7 +239,7 @@ var _ = Describe("Normalization", func() { Expect(string(n)).To(Equal(`{"component":{"componentReferences":[],"name":"github.com/vasu1124/introspect","provider":{"name":"internal"},"resources":[{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6a1c7637a528ab5957ab60edf73b5298a0a03de02a96be0313ee89b22544840c"},"labels":[{"name":"label2","signing":true,"value":"bar"}],"name":"introspect-image","relation":"local","type":"ociImage","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"genericBlobDigest/v1","value":"d1187ac17793b2f5fa26175c21cabb6ce388871ae989e16ff9a38bd6b32507bf"},"name":"introspect-blueprint","relation":"local","type":"landscaper.gardener.cloud/blueprint","version":"1.0.0"},{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"ociArtifactDigest/v1","value":"6229be2be7e328f74ba595d93b814b590b1aa262a1b85e49cc1492795a9e564c"},"name":"introspect-helm","relation":"external","type":"helm","version":"0.1.0"},{"name":"none","relation":"local","type":"plainText","version":"v1"}],"sources":[{"name":"introspect","type":"git","version":"1.0.0"}],"version":"1.0.0"}}`)) }) - It("hashes v2 with complex provider", func() { + It("normalizes v2 with complex provider", func() { cd := cd1.Copy() cd.References = nil cd.Resources = nil @@ -248,7 +257,7 @@ var _ = Describe("Normalization", func() { Expect(string(n)).To(Equal(`{"component":{"componentReferences":[],"labels":[{"name":"non-volatile","signing":true,"value":"comp-value2"}],"name":"github.com/vasu1124/introspect","provider":{"labels":[{"name":"non-volatile","signing":true,"value":"prov-value2"}],"name":"internal"},"resources":[],"sources":[],"version":"1.0.0"}}`)) }) - It("hashes v1 with complex provider for CD/v2", func() { + It("normalizes v1 with complex provider for CD/v2", func() { cd := cd1.Copy() cd.References = nil cd.Resources = nil @@ -266,7 +275,7 @@ var _ = Describe("Normalization", func() { Expect(string(n)).To(StringEqualWithContext(`[{"component":[{"componentReferences":[]},{"labels":[[{"name":"non-volatile"},{"signing":true},{"value":"comp-value2"}]]},{"name":"github.com/vasu1124/introspect"},{"provider":[{"labels":[[{"name":"non-volatile"},{"signing":true},{"value":"prov-value2"}]]},{"name":"internal"}]},{"resources":[]},{"version":"1.0.0"}]},{"meta":[{"schemaVersion":"v2"}]}]`)) }) - It("hashes v1 with complex provider for CD/v3", func() { + It("normalizes v1 with complex provider for CD/v3", func() { cd := cd1.Copy() cd.Metadata.ConfiguredVersion = v3alpha1.SchemaVersion cd.References = nil @@ -284,4 +293,51 @@ var _ = Describe("Normalization", func() { Expect(string(n)).To(StringEqualWithContext(`[{"apiVersion":"ocm.software/v3alpha1"},{"kind":"ComponentVersion"},{"metadata":[{"labels":[[{"name":"non-volatile"},{"signing":true},{"value":"comp-value2"}]]},{"name":"github.com/vasu1124/introspect"},{"provider":[{"labels":[[{"name":"volatile"},{"value":"prov-value1"}],[{"name":"non-volatile"},{"signing":true},{"value":"prov-value2"}]]},{"name":"internal"}]},{"version":"1.0.0"}]},{"spec":[]}]`)) }) + + Context("normalization and legacy extra identity defaulting", func() { + var cd *compdesc.ComponentDescriptor + BeforeEach(func() { + cd = Must(compdesc.Decode([]byte(` + component: + version: 1.0.0 + componentReferences: [] + name: ocm.software/duplicate-resource/test + provider: internal + repositoryContexts: [] + resources: + - name: image + relation: local + type: ociImage + version: 1.0.0 + access: + imageReference: ghcr.io/bla:1.0.0 + type: ociRegistry + - name: image + relation: local + type: ociImage + version: 2.0.0 + access: + imageReference: ghcr.io/bla:2.0.0 + type: ociRegistry + sources: [] + meta: + schemaVersion: v2 +`))) + }) + It("normalizes v1 with extra identity defaulting", func() { + n := Must(compdesc.Normalize(cd, compdesc.JsonNormalisationV1)) + Expect(string(n)).To(StringEqualTrimmedWithContext("[{\"component\":[{\"componentReferences\":[]},{\"name\":\"ocm.software/duplicate-resource/test\"},{\"provider\":\"internal\"},{\"resources\":[[{\"extraIdentity\":[{\"version\":\"1.0.0\"}]},{\"name\":\"image\"},{\"relation\":\"local\"},{\"type\":\"ociImage\"},{\"version\":\"1.0.0\"}],[{\"extraIdentity\":null},{\"name\":\"image\"},{\"relation\":\"local\"},{\"type\":\"ociImage\"},{\"version\":\"2.0.0\"}]]},{\"version\":\"1.0.0\"}]},{\"meta\":[{\"schemaVersion\":\"v2\"}]}]")) + Expect(string(n)).To(ContainSubstring("\"extraIdentity\":[{\"version\":\"1.0.0\"}]"), "extra identity should have been defaulted, see api/ocm/compdesc/normalizations/legacy/DefaultingOfVersionIntoExtraIdentity") + }) + It("normalizes v2 with extra identity defaulting", func() { + n := Must(compdesc.Normalize(cd, compdesc.JsonNormalisationV2)) + Expect(string(n)).To(StringEqualTrimmedWithContext("{\"component\":{\"componentReferences\":[],\"name\":\"ocm.software/duplicate-resource/test\",\"provider\":{\"name\":\"internal\"},\"resources\":[{\"extraIdentity\":{\"version\":\"1.0.0\"},\"name\":\"image\",\"relation\":\"local\",\"type\":\"ociImage\",\"version\":\"1.0.0\"},{\"name\":\"image\",\"relation\":\"local\",\"type\":\"ociImage\",\"version\":\"2.0.0\"}],\"sources\":[],\"version\":\"1.0.0\"}}")) + Expect(string(n)).To(ContainSubstring("{\"extraIdentity\":{\"version\":\"1.0.0\"}"), "extra identity should have been defaulted, see api/ocm/compdesc/normalizations/legacy/DefaultingOfVersionIntoExtraIdentity") + }) + It("normalizes v3 without extra identity defaulting", func() { + n := Must(compdesc.Normalize(cd, compdesc.JsonNormalisationV3)) + Expect(string(n)).To(StringEqualTrimmedWithContext("{\"component\":{\"componentReferences\":[],\"name\":\"ocm.software/duplicate-resource/test\",\"provider\":{\"name\":\"internal\"},\"resources\":[{\"name\":\"image\",\"relation\":\"local\",\"type\":\"ociImage\",\"version\":\"1.0.0\"},{\"name\":\"image\",\"relation\":\"local\",\"type\":\"ociImage\",\"version\":\"2.0.0\"}],\"sources\":[],\"version\":\"1.0.0\"}}")) + Expect(string(n)).ToNot(ContainSubstring("{\"extraIdentity\":{\"version\":\"1.0.0\"}"), "extra identity should not have been defaulted") + }) + }) }) diff --git a/api/ocm/compdesc/normalization.go b/api/ocm/compdesc/normalization.go index 5084f1ca89..6e7687d5cb 100644 --- a/api/ocm/compdesc/normalization.go +++ b/api/ocm/compdesc/normalization.go @@ -11,8 +11,11 @@ import ( type NormalisationAlgorithm = string const ( + // Deprecated: use JsonNormalisationV3 instead JsonNormalisationV1 NormalisationAlgorithm = "jsonNormalisation/v1" + // Deprecated: use JsonNormalisationV3 instead JsonNormalisationV2 NormalisationAlgorithm = "jsonNormalisation/v2" + JsonNormalisationV3 NormalisationAlgorithm = "jsonNormalisation/v3" ) type Normalization interface { diff --git a/api/ocm/compdesc/normalizations/init.go b/api/ocm/compdesc/normalizations/init.go index 96dab531de..6b4eeefa3e 100644 --- a/api/ocm/compdesc/normalizations/init.go +++ b/api/ocm/compdesc/normalizations/init.go @@ -3,4 +3,5 @@ package normalizations import ( _ "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv1" _ "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv2" + _ "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv3" ) diff --git a/api/ocm/compdesc/normalizations/jsonv1/norm.go b/api/ocm/compdesc/normalizations/jsonv1/norm.go index 5a6f9bf141..ae24494fa2 100644 --- a/api/ocm/compdesc/normalizations/jsonv1/norm.go +++ b/api/ocm/compdesc/normalizations/jsonv1/norm.go @@ -8,9 +8,11 @@ import ( "github.com/mandelsoft/goutils/errors" "ocm.software/ocm/api/ocm/compdesc" + "ocm.software/ocm/api/ocm/compdesc/normalizations/legacy" "ocm.software/ocm/api/utils/errkind" ) +// Deprecated: use compdesc.JsonNormalisationV3 instead const Algorithm = compdesc.JsonNormalisationV1 func init() { @@ -20,11 +22,10 @@ func init() { type normalization struct{} func (m normalization) Normalize(cd *compdesc.ComponentDescriptor) ([]byte, error) { + legacy.DefaultingOfVersionIntoExtraIdentity(cd) cv := compdesc.DefaultSchemes[cd.SchemaVersion()] if cv == nil { - if cv == nil { - return nil, errors.ErrNotSupported(errkind.KIND_SCHEMAVERSION, cd.SchemaVersion()) - } + return nil, errors.ErrNotSupported(errkind.KIND_SCHEMAVERSION, cd.SchemaVersion()) } v, err := cv.ConvertFrom(cd) if err != nil { diff --git a/api/ocm/compdesc/normalizations/jsonv2/norm.go b/api/ocm/compdesc/normalizations/jsonv2/norm.go index 1fcb3a98e8..9fb7b3f284 100644 --- a/api/ocm/compdesc/normalizations/jsonv2/norm.go +++ b/api/ocm/compdesc/normalizations/jsonv2/norm.go @@ -10,11 +10,13 @@ package jsonv2 import ( "ocm.software/ocm/api/ocm/compdesc" + "ocm.software/ocm/api/ocm/compdesc/normalizations/legacy" "ocm.software/ocm/api/ocm/compdesc/normalizations/rules" "ocm.software/ocm/api/tech/signing" "ocm.software/ocm/api/tech/signing/norm/jcs" ) +// Deprecated: use compdesc.JsonNormalisationV3 instead const Algorithm = compdesc.JsonNormalisationV2 func init() { @@ -24,6 +26,7 @@ func init() { type normalization struct{} func (m normalization) Normalize(cd *compdesc.ComponentDescriptor) ([]byte, error) { + legacy.DefaultingOfVersionIntoExtraIdentity(cd) data, err := signing.Normalize(jcs.Type, cd, CDExcludes) return data, err } diff --git a/api/ocm/compdesc/normalizations/jsonv3/norm.go b/api/ocm/compdesc/normalizations/jsonv3/norm.go new file mode 100644 index 0000000000..f8092920b0 --- /dev/null +++ b/api/ocm/compdesc/normalizations/jsonv3/norm.go @@ -0,0 +1,31 @@ +// Package jsonv3 provides a normalization which is completely based on the +// abstract (internal) version of the component descriptor and is therefore +// agnostic of the final serialization format. Signatures using this algorithm +// can be transferred among different schema versions, as long as is able to +// handle the complete information using for the normalization. +// jsonv2 is the predecessor of this version but had internal defaulting logic +// that is no longer included as part of this normalization. Thus v3 should be preferred over v2. +// Note that between v2 and v3 differences can occur mainly if the "extra identity" field is not unique, +// in which case the v2 normalization opinionated on how to differentiate these items. This no longer +// happens in v3, meaning the component descriptor is normalized as is. +package jsonv3 + +import ( + "ocm.software/ocm/api/ocm/compdesc" + "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv2" + "ocm.software/ocm/api/tech/signing" + "ocm.software/ocm/api/tech/signing/norm/jcs" +) + +const Algorithm = compdesc.JsonNormalisationV3 + +func init() { + compdesc.Normalizations.Register(Algorithm, normalization{}) +} + +type normalization struct{} + +func (m normalization) Normalize(cd *compdesc.ComponentDescriptor) ([]byte, error) { + data, err := signing.Normalize(jcs.Type, cd, jsonv2.CDExcludes) + return data, err +} diff --git a/api/ocm/compdesc/normalizations/legacy/legacy.go b/api/ocm/compdesc/normalizations/legacy/legacy.go new file mode 100644 index 0000000000..e05c982035 --- /dev/null +++ b/api/ocm/compdesc/normalizations/legacy/legacy.go @@ -0,0 +1,57 @@ +package legacy + +import ( + "fmt" + + "ocm.software/ocm/api/ocm/compdesc" + "ocm.software/ocm/api/ocm/selectors/accessors" + "ocm.software/ocm/api/utils/logging" +) + +var ( + REALM = logging.DefineSubRealm("component descriptor legacy normalization defaulting", "compdesc", "normalizations", "legacy") + Logger = logging.DynamicLogger(REALM) +) + +// DefaultingOfVersionIntoExtraIdentity normalizes the extra identity of the resources. +// It sets the version of the resource, reference or source as extra identity field if the combination of name+extra identity +// is the same for multiple items. However, the last item in the list will not be updated as it is unique wihout this. +// +// TODO: To be removed once v1 + v2 are removed. +// +// Deprecated: This is a legacy normalization and should only be used as part of JsonNormalisationV1 and JsonNormalisationV2 +// for backwards compatibility of normalization (for example used for signatures). It was needed because the original +// defaulting was made part of the normalization by accident and is now no longer included by default due to +// https://github.com/open-component-model/ocm/pull/1026 +func DefaultingOfVersionIntoExtraIdentity(cd *compdesc.ComponentDescriptor) { + resources := make([]accessors.ElementMeta, len(cd.Resources)) + for i := range cd.Resources { + resources[i] = &cd.Resources[i] + } + defaultingOfVersionIntoExtraIdentity(resources) +} + +func defaultingOfVersionIntoExtraIdentity(meta []accessors.ElementMeta) { + for i := range meta { + for j := range meta { + // don't match with itself and only match with the same name + if meta[j].GetName() != meta[i].GetName() || i == j { + continue + } + + eid := meta[i].GetExtraIdentity() + // if the extra identity is not the same, then there is not a clash + if !meta[j].GetExtraIdentity().Equals(eid) { + continue + } + + eid.Set(compdesc.SystemIdentityVersion, meta[i].GetVersion()) + meta[i].GetMeta().SetExtraIdentity(eid) + + Logger.Warn(fmt.Sprintf("resource identity duplication was normalized for backwards compatibility, "+ + "to avoid this either specify a unique extra identity per item or switch to %s", compdesc.JsonNormalisationV3), + "name", meta[i].GetName(), "index", i, "extra identity", meta[i].GetExtraIdentity()) + break + } + } +} diff --git a/api/ocm/selectors/accessors/accessors.go b/api/ocm/selectors/accessors/accessors.go index fa8b776487..9fa3c8e00a 100644 --- a/api/ocm/selectors/accessors/accessors.go +++ b/api/ocm/selectors/accessors/accessors.go @@ -27,6 +27,7 @@ type ElementMeta interface { GetMeta() ElementMeta // ElementMeta is again a Meta provider SetLabels(labels []v1.Label) + SetExtraIdentity(identity v1.Identity) } // ElementMetaProvider just provides access to element meta data diff --git a/cmds/ocm/commands/ocmcmds/common/options/hashoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/hashoption/option.go index 4eb900f449..04a755e25e 100644 --- a/cmds/ocm/commands/ocmcmds/common/options/hashoption/option.go +++ b/cmds/ocm/commands/ocmcmds/common/options/hashoption/option.go @@ -7,6 +7,8 @@ import ( clictx "ocm.software/ocm/api/cli" "ocm.software/ocm/api/ocm/compdesc" "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv1" + "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv2" + "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv3" "ocm.software/ocm/api/ocm/extensions/attrs/signingattr" ocmsign "ocm.software/ocm/api/ocm/tools/signing" "ocm.software/ocm/api/tech/signing" @@ -34,13 +36,13 @@ type Option struct { } func (o *Option) AddFlags(fs *pflag.FlagSet) { - fs.StringVarP(&o.NormAlgorithm, "normalization", "N", jsonv1.Algorithm, "normalization algorithm") + fs.StringVarP(&o.NormAlgorithm, "normalization", "N", jsonv3.Algorithm, "normalization algorithm") fs.StringVarP(&o.hashAlgorithm, "hash", "H", sha256.Algorithm, "hash algorithm") } func (o *Option) Configure(ctx clictx.Context) error { if o.NormAlgorithm == "" { - o.NormAlgorithm = jsonv1.Algorithm + o.NormAlgorithm = jsonv3.Algorithm } if o.hashAlgorithm == "" { o.hashAlgorithm = sha256.Algorithm @@ -59,7 +61,18 @@ func (o *Option) Configure(ctx clictx.Context) error { func (o *Option) Usage() string { s := ` The following normalization modes are supported with option --normalization: -` + listformat.FormatList(jsonv1.Algorithm, compdesc.Normalizations.Names()...) +` + listformat.FormatList(jsonv3.Algorithm, compdesc.Normalizations.Names()...) + + s += ` + +Note that the normalization algorithm is important to be equivalent when used for signing and verification, otherwise +the verification can fail. Please always migrate to the latest normalization algorithm whenever possible. +New signature algorithms can be used as soon as they are available in the component version after signing it. + +The algorithms ` + jsonv1.Algorithm + ` and ` + jsonv2.Algorithm + ` are deprecated and should not be used anymore. +Please switch to ` + jsonv3.Algorithm + ` as soon as possible. + +` s += ` diff --git a/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go index 14723d097a..a0ac7fdb22 100644 --- a/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go +++ b/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go @@ -9,7 +9,7 @@ import ( clictx "ocm.software/ocm/api/cli" "ocm.software/ocm/api/ocm/compdesc" - "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv1" + "ocm.software/ocm/api/ocm/compdesc/normalizations/jsonv3" "ocm.software/ocm/api/ocm/extensions/attrs/signingattr" ocmsign "ocm.software/ocm/api/ocm/tools/signing" "ocm.software/ocm/api/tech/signing" @@ -151,7 +151,7 @@ The following signing types are supported with option --algorithm: s += ` The following normalization modes are supported with option --normalization: -` + listformat.FormatList(jsonv1.Algorithm, compdesc.Normalizations.Names()...) +` + listformat.FormatList(jsonv3.Algorithm, compdesc.Normalizations.Names()...) s += ` diff --git a/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go index f79e2c1c05..6f20c09d0c 100644 --- a/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go @@ -9,6 +9,7 @@ import ( . "github.com/mandelsoft/goutils/testutils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "ocm.software/ocm/cmds/ocm/testhelper" "ocm.software/ocm/api/ocm/compdesc" @@ -38,41 +39,47 @@ var _ = Describe("Test Environment", func() { env.Cleanup() }) - It("hash component archive", func() { - env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { - env.Provider(PROVIDER) - }) - - buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-o", "wide")).To(Succeed()) - Expect(buf.String()).To(StringEqualTrimmedWithContext( - ` -COMPONENT VERSION HASH NORMALIZED FORM -test.de/x v1 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b [{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] -`)) - }) - - It("normalize component archive v1", func() { - env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { - env.Provider(PROVIDER) - }) + DescribeTable("should hash the component archive with specified parameters", + func(normalizationMethod string, expectedOutput string) { + env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { + env.Provider(PROVIDER) + }) - buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-O", "-", "-o", "norm")).To(Succeed()) - Expect(buf.String()).To(Equal(`[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] -`)) - }) + buf := bytes.NewBuffer(nil) + cmdArgs := []string{"hash", "components", ARCH, "-o", "wide", "--normalization", normalizationMethod} + Expect(env.CatchOutput(buf).Execute(cmdArgs...)).To(Succeed()) + Expect(buf.String()).To(StringEqualTrimmedWithContext(expectedOutput)) + }, - It("normalize component archive v2", func() { + Entry("v1", compdesc.JsonNormalisationV1, + `COMPONENT VERSION HASH NORMALIZED FORM +test.de/x v1 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b [{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] +`), + Entry("v2", compdesc.JsonNormalisationV2, + `COMPONENT VERSION HASH NORMALIZED FORM +test.de/x v1 33aeb8c46ea4bacbf0bc3ac42c186c7f5e313584601a93bf861c016d73c9e4f1 {"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} +`), + Entry("v3", compdesc.JsonNormalisationV3, + `COMPONENT VERSION HASH NORMALIZED FORM +test.de/x v1 33aeb8c46ea4bacbf0bc3ac42c186c7f5e313584601a93bf861c016d73c9e4f1 {"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} +`), + ) + + DescribeTable("normalize component archive", func(normalizationMethod string, expectedOutput string) { env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { env.Provider(PROVIDER) }) buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-N", "jsonNormalisation/v2", "-o", "norm")).To(Succeed()) - Expect(buf.String()).To(StringEqualTrimmedWithContext(`{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} + Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-O", "-", "-o", "norm", "-N", normalizationMethod)).To(Succeed()) + Expect(buf.String()).To(Equal(expectedOutput)) + }, + Entry("v1", compdesc.JsonNormalisationV1, `[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] +`), + Entry("v2", compdesc.JsonNormalisationV2, `{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} +`), + Entry("v3", compdesc.JsonNormalisationV3, `{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} `)) - }) It("check hash", func() { env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { @@ -85,16 +92,16 @@ test.de/x v1 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b --- component: test.de/x context: [] -hash: 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b -normalized: '[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}]' +hash: 33aeb8c46ea4bacbf0bc3ac42c186c7f5e313584601a93bf861c016d73c9e4f1 +normalized: '{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}}' version: v1 `)) - h := sha256.Sum256([]byte(`[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}]`)) - Expect(hex.EncodeToString(h[:])).To(Equal("37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b")) + h := sha256.Sum256([]byte(`{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}}`)) + Expect(hex.EncodeToString(h[:])).To(Equal("33aeb8c46ea4bacbf0bc3ac42c186c7f5e313584601a93bf861c016d73c9e4f1")) }) - It("hash component archive with resources", func() { + DescribeTable("hash component archive with resources", func(normalizationMethod string, expectedOutput string) { env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { env.Provider(PROVIDER) env.Resource("test", VERSION, resourcetypes.PLAIN_TEXT, metav1.LocalRelation, func() { @@ -103,34 +110,27 @@ version: v1 }) buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-o", "wide")).To(Succeed()) - Expect(buf.String()).To(StringEqualTrimmedWithContext( - ` + Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-o", "wide", "-N", normalizationMethod)).To(Succeed()) + Expect(buf.String()).To(StringEqualTrimmedWithContext(expectedOutput)) + }, + Entry("v1", compdesc.JsonNormalisationV1, ` COMPONENT : test.de/x VERSION : v1 HASH : 9d8fc24cf27d1092f58098286d9f63c6824c2daf739c19789f64c062d1f30cc5 NORMALIZED FORM: [{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[[{"digest":[{"hashAlgorithm":"SHA-256"},{"normalisationAlgorithm":"genericBlobDigest/v1"},{"value":"810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50"}]},{"extraIdentity":null},{"name":"test"},{"relation":"local"},{"type":"plainText"},{"version":"v1"}]]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] ----`)) - }) - - It("hash component archive with resources", func() { - env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { - env.Provider(PROVIDER) - env.Resource("test", VERSION, resourcetypes.PLAIN_TEXT, metav1.LocalRelation, func() { - env.BlobStringData(mime.MIME_TEXT, "testdata") - }) - }) - - buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "--actual", "-o", "wide")).To(Succeed()) - Expect(buf.String()).To(StringEqualTrimmedWithContext( - ` +---`), + Entry("v2", compdesc.JsonNormalisationV2, ` COMPONENT : test.de/x VERSION : v1 -HASH : 9d8fc24cf27d1092f58098286d9f63c6824c2daf739c19789f64c062d1f30cc5 -NORMALIZED FORM: [{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[[{"digest":[{"hashAlgorithm":"SHA-256"},{"normalisationAlgorithm":"genericBlobDigest/v1"},{"value":"810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50"}]},{"extraIdentity":null},{"name":"test"},{"relation":"local"},{"type":"plainText"},{"version":"v1"}]]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] +HASH : 6e8e9eb0af1c4c0b9dcc4161168b3f0ad913bc85e4234688dd6d4b283fe4b956 +NORMALIZED FORM: {"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"genericBlobDigest/v1","value":"810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50"},"name":"test","relation":"local","type":"plainText","version":"v1"}],"sources":[],"version":"v1"}} +---`), + Entry("v3", compdesc.JsonNormalisationV3, ` +COMPONENT : test.de/x +VERSION : v1 +HASH : 6e8e9eb0af1c4c0b9dcc4161168b3f0ad913bc85e4234688dd6d4b283fe4b956 +NORMALIZED FORM: {"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[{"digest":{"hashAlgorithm":"SHA-256","normalisationAlgorithm":"genericBlobDigest/v1","value":"810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50"},"name":"test","relation":"local","type":"plainText","version":"v1"}],"sources":[],"version":"v1"}} ---`)) - }) It("hash component archive with v2", func() { env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { @@ -151,7 +151,7 @@ NORMALIZED FORM: {"component":{"componentReferences":[],"name":"test.de/x","prov ---`)) }) - It("hash component recursively", func() { + It("hash partial component archive recursively", func() { env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { env.ComponentVersion(COMP2, VERSION, func() { env.Provider(PROVIDER) @@ -166,32 +166,12 @@ NORMALIZED FORM: {"component":{"componentReferences":[],"name":"test.de/x","prov Expect(env.CatchOutput(buf).Execute("hash", "components", "-r", ARCH+"//test.de/x:v1")).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` REFERENCEPATH COMPONENT VERSION HASH IDENTITY - test.de/x v1 b74cee6c6b8215f470efd0e3c49618bb98610fc80de36a2e121d0550650b9cdc -test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a "name"="ref" -`)) - }) - - It("hash component recursively", func() { - env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { - env.ComponentVersion(COMP2, VERSION, func() { - env.Provider(PROVIDER) - }) - env.ComponentVersion(COMP, VERSION, func() { - env.Provider(PROVIDER) - env.Reference("ref", COMP2, VERSION) - }) - }) - - buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("hash", "components", "-r", "--repo", ARCH, "test.de/x:v1")).To(Succeed()) - Expect(buf.String()).To(StringEqualTrimmedWithContext(` -REFERENCEPATH COMPONENT VERSION HASH IDENTITY - test.de/x v1 b74cee6c6b8215f470efd0e3c49618bb98610fc80de36a2e121d0550650b9cdc -test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a "name"="ref" + test.de/x v1 4ca827281d94cdfee77e7ab2d89164f5e0d38890d12f035c2cbe19d72851fb17 +test.de/x:v1 test.de/y v1 bd420aea257660f444b39165cee42b905e48165021781e2468dcf8e1cbc0151d "name"="ref" `)) }) - It("hash components recursively", func() { + It("hash component archive recursively", func() { env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { env.ComponentVersion(COMP2, VERSION, func() { env.Provider(PROVIDER) @@ -206,9 +186,9 @@ test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd7218 Expect(env.CatchOutput(buf).Execute("hash", "components", "-r", ARCH)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` REFERENCEPATH COMPONENT VERSION HASH IDENTITY - test.de/x v1 b74cee6c6b8215f470efd0e3c49618bb98610fc80de36a2e121d0550650b9cdc -test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a "name"="ref" - test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a + test.de/x v1 4ca827281d94cdfee77e7ab2d89164f5e0d38890d12f035c2cbe19d72851fb17 +test.de/x:v1 test.de/y v1 bd420aea257660f444b39165cee42b905e48165021781e2468dcf8e1cbc0151d "name"="ref" + test.de/y v1 bd420aea257660f444b39165cee42b905e48165021781e2468dcf8e1cbc0151d `)) }) @@ -227,8 +207,8 @@ test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd7218 Expect(env.CatchOutput(buf).Execute("hash", "components", "-r", "--repo", ARCH, "-U", "test.de/x:v1")).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` REFERENCEPATH COMPONENT VERSION HASH IDENTITY - test.de/x v1 b74cee6c6b8215f470efd0e3c49618bb98610fc80de36a2e121d0550650b9cdc -test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a "name"="ref" + test.de/x v1 4ca827281d94cdfee77e7ab2d89164f5e0d38890d12f035c2cbe19d72851fb17 +test.de/x:v1 test.de/y v1 bd420aea257660f444b39165cee42b905e48165021781e2468dcf8e1cbc0151d "name"="ref" `)) repo := Must(ctf.Open(env, ctf.ACC_READONLY, ARCH, 0, env)) @@ -247,6 +227,6 @@ test.de/x:v1 test.de/y v1 e60c791a20091abcf8d35742a134b3a99ce811d874fd7218 ref := Must(cv.GetReferenceByIndex(0)) d := ref.GetDigest() Expect(d).NotTo(BeNil()) - Expect(d.Value).To(Equal("e60c791a20091abcf8d35742a134b3a99ce811d874fd721870b28ea90ef5ad2a")) + Expect(d.Value).To(Equal("bd420aea257660f444b39165cee42b905e48165021781e2468dcf8e1cbc0151d")) }) }) diff --git a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go index 0537457516..7991b3f843 100644 --- a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go @@ -7,7 +7,9 @@ import ( . "github.com/mandelsoft/goutils/testutils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "ocm.software/ocm/api/oci/testhelper" + "ocm.software/ocm/api/ocm/compdesc" . "ocm.software/ocm/api/ocm/testhelper" . "ocm.software/ocm/cmds/ocm/testhelper" @@ -52,19 +54,50 @@ const ( ) const ( - D_COMPONENTA = "01de99400030e8336020059a435cea4e7fe8f21aad4faf619da882134b85569d" - D_COMPONENTB = "5f416ec59629d6af91287e2ba13c6360339b6a0acf624af2abd2a810ce4aefce" + D_COMPONENTA_V1 = "01de99400030e8336020059a435cea4e7fe8f21aad4faf619da882134b85569d" + D_COMPONENTB_V1 = "5f416ec59629d6af91287e2ba13c6360339b6a0acf624af2abd2a810ce4aefce" ) const VERIFIED_FILE = "verified.yaml" -var substitutions = Substitutions{ - "test": D_COMPONENTA, - "r0": D_TESTDATA, - "r1": DS_OCIMANIFEST1.Value, - "r2": DS_OCIMANIFEST2.Value, - "ref": D_COMPONENTB, - "rb0": D_OTHERDATA, +var substitutionsV1 = Substitutions{ + "test": D_COMPONENTA_V1, + "r0": D_TESTDATA, + "r1": DS_OCIMANIFEST1.Value, + "r2": DS_OCIMANIFEST2.Value, + "ref": D_COMPONENTB_V1, + "rb0": D_OTHERDATA, + "normAlgo": compdesc.JsonNormalisationV1, +} + +const ( + D_COMPONENTA_V2 = "10ac0b3a850e1f1becf56d5d45e9742fa0a91103d25ba93cc3a509f68797e90f" + D_COMPONENTB_V2 = "1ae74420ef29436ad75133d81bceb59fa8ef1e2ce083a45b5f4baaec641a4266" +) + +var substitutionsV2 = Substitutions{ + "test": D_COMPONENTA_V2, + "r0": D_TESTDATA, + "r1": DS_OCIMANIFEST1.Value, + "r2": DS_OCIMANIFEST2.Value, + "ref": D_COMPONENTB_V2, + "rb0": D_OTHERDATA, + "normAlgo": compdesc.JsonNormalisationV2, +} + +const ( + D_COMPONENTA_V3 = D_COMPONENTA_V2 + D_COMPONENTB_V3 = "766f26b09237f9647714e85fac914f115d0b4c3277b01ec00cfeb3b50a68cde9" +) + +var substitutionsV3 = Substitutions{ + "test": D_COMPONENTA_V3, + "r0": D_TESTDATA, + "r1": DS_OCIMANIFEST1.Value, + "r2": DS_OCIMANIFEST2.Value, + "ref": D_COMPONENTB_V3, + "rb0": D_OTHERDATA, + "normAlgo": compdesc.JsonNormalisationV3, } var _ = Describe("access method", func() { @@ -116,7 +149,7 @@ var _ = Describe("access method", func() { prepareEnv(env, ARCH, "") buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTA+":"+VERSION)).To(Succeed()) + Expect(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTA+":"+VERSION, "--normalization", compdesc.JsonNormalisationV1)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v1]... @@ -124,7 +157,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v resource 1: "name"="value": digest SHA-256:${r1}[ociArtifactDigest/v1] resource 2: "name"="ref": digest SHA-256:${r2}[ociArtifactDigest/v1] successfully signed github.com/mandelsoft/test:v1 (digest SHA-256:${test})`, - substitutions), + substitutionsV1), ) session := datacontext.NewSession() @@ -136,14 +169,14 @@ successfully signed github.com/mandelsoft/test:v1 (digest SHA-256:${test})`, cv, err := src.LookupComponentVersion(COMPONENTA, VERSION) Expect(err).To(Succeed()) session.AddCloser(cv) - Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTA)) + Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTA_V1)) }) It("signs transport archive", func() { prepareEnv(env, ARCH, ARCH) buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION)).To(Succeed()) + Expect(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION, "--normalization", compdesc.JsonNormalisationV1)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]... @@ -155,7 +188,7 @@ applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1] reference 0: github.com/mandelsoft/test:v1: digest SHA-256:${test}[jsonNormalisation/v1] resource 0: "name"="otherdata": digest SHA-256:${rb0}[genericBlobDigest/v1] successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) -`, substitutions)) +`, substitutionsV1)) session := datacontext.NewSession() defer session.Close() @@ -166,14 +199,14 @@ successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) cv, err := src.LookupComponentVersion(COMPONENTB, VERSION) Expect(err).To(Succeed()) session.AddCloser(cv) - Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTB)) + Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTB_V1)) }) It("signs transport archive with --lookup option", func() { prepareEnv(env, ARCH2, ARCH) buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("sign", "components", "--lookup", ARCH2, "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION)).To(Succeed()) + Expect(env.CatchOutput(buf).Execute("sign", "components", "--lookup", ARCH2, "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION, "--normalization", compdesc.JsonNormalisationV1)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]... @@ -185,7 +218,7 @@ applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1] reference 0: github.com/mandelsoft/test:v1: digest SHA-256:${test}[jsonNormalisation/v1] resource 0: "name"="otherdata": digest SHA-256:${rb0}[genericBlobDigest/v1] successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) -`, substitutions)) +`, substitutionsV1)) session := datacontext.NewSession() defer session.Close() @@ -196,7 +229,7 @@ successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) cv, err := src.LookupComponentVersion(COMPONENTB, VERSION) Expect(err).To(Succeed()) session.AddCloser(cv) - Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTB)) + Expect(cv.GetDescriptor().Signatures[0].Digest.Value).To(Equal(D_COMPONENTB_V1)) }) }) @@ -281,7 +314,7 @@ Error: signing: github.com/mandelsoft/ref:v1: failed resolving component referen It("signs comp arch with lookup", func() { buf := bytes.NewBuffer(nil) - MustBeSuccessful(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--lookup", ARCH, "--repo", COMPARCH)) + MustBeSuccessful(env.CatchOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--lookup", ARCH, "--repo", COMPARCH, "--normalization", compdesc.JsonNormalisationV1)) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]... no digest found for "github.com/mandelsoft/test:v1" @@ -330,7 +363,7 @@ created rsa key pair key.priv[key.cert] // sigh component with certificate buf.Reset() - Expect(env.CatchOutput(buf).Execute("sign", "component", ARCH, "-K", "key.priv", "-k", "key.cert", "--ca-cert", "root.cert", "-s", "mandelsoft", "-I", "CN=mandelsoft")).To(Succeed()) + Expect(env.CatchOutput(buf).Execute("sign", "component", ARCH, "-K", "key.priv", "-k", "key.cert", "--ca-cert", "root.cert", "-s", "mandelsoft", "-I", "CN=mandelsoft", "--normalization", compdesc.JsonNormalisationV1)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v1]... successfully signed github.com/mandelsoft/test:v1 (digest SHA-256:5ed8bb27309c3c2fff43f3b0f3ebb56a5737ad6db4bc8ace73c5455cb86faf54) @@ -371,11 +404,11 @@ successfully verified github.com/mandelsoft/test:v1 (digest SHA-256:5ed8bb27309c }) }) - It("signs transport archive", func() { + DescribeTable("signs transport archive", func(substitutions Substitutions, normAlgo string) { prepareEnv(env, ARCH, ARCH) buf := bytes.NewBuffer(nil) - Expect(env.CatchOutput(buf).Execute("sign", "components", "--verified", VERIFIED_FILE, "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION)).To(Succeed()) + Expect(env.CatchOutput(buf).Execute("sign", "components", "--verified", VERIFIED_FILE, "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION, "--normalization", normAlgo)).To(Succeed()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]... @@ -384,7 +417,7 @@ applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1] resource 0: "name"="testdata": digest SHA-256:${r0}[genericBlobDigest/v1] resource 1: "name"="value": digest SHA-256:${r1}[ociArtifactDigest/v1] resource 2: "name"="ref": digest SHA-256:${r2}[ociArtifactDigest/v1] - reference 0: github.com/mandelsoft/test:v1: digest SHA-256:${test}[jsonNormalisation/v1] + reference 0: github.com/mandelsoft/test:v1: digest SHA-256:${test}[${normAlgo}] resource 0: "name"="otherdata": digest SHA-256:${rb0}[genericBlobDigest/v1] successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) `, substitutions)) @@ -395,7 +428,11 @@ successfully signed github.com/mandelsoft/ref:v1 (digest SHA-256:${ref}) CheckStore(store, common.NewNameVersion(COMPONENTA, VERSION)) CheckStore(store, common.NewNameVersion(COMPONENTB, VERSION)) - }) + }, + Entry("v1", substitutionsV1, compdesc.JsonNormalisationV1), + Entry("v2", substitutionsV2, compdesc.JsonNormalisationV2), + Entry("v3", substitutionsV3, compdesc.JsonNormalisationV3), + ) }) }) diff --git a/docs/reference/ocm_hash_componentversions.md b/docs/reference/ocm_hash_componentversions.md index 1e49ba07f4..4c5e46bdca 100644 --- a/docs/reference/ocm_hash_componentversions.md +++ b/docs/reference/ocm_hash_componentversions.md @@ -21,7 +21,7 @@ componentversions, componentversion, cv, components, component, comps, comp, c -h, --help help for componentversions --latest restrict component versions to latest --lookup stringArray repository name or spec for closure lookup fallback - -N, --normalization string normalization algorithm (default "jsonNormalisation/v1") + -N, --normalization string normalization algorithm (default "jsonNormalisation/v3") -O, --outfile string Output file for normalized component descriptor (default "-") -o, --output string output mode (JSON, json, norm, wide, yaml) -r, --recursive follow component reference nesting @@ -63,8 +63,18 @@ references. The following normalization modes are supported with option --normalization: - - jsonNormalisation/v1 (default) + - jsonNormalisation/v1 - jsonNormalisation/v2 + - jsonNormalisation/v3 (default) + + +Note that the normalization algorithm is important to be equivalent when used for signing and verification, otherwise +the verification can fail. Please always migrate to the latest normalization algorithm whenever possible. +New signature algorithms can be used as soon as they are available in the component version after signing it. + +The algorithms jsonNormalisation/v1 and jsonNormalisation/v2 are deprecated and should not be used anymore. +Please switch to jsonNormalisation/v3 as soon as possible. + The following hash modes are supported with option --hash: diff --git a/docs/reference/ocm_logging.md b/docs/reference/ocm_logging.md index 9a1c45800b..3e579d5488 100644 --- a/docs/reference/ocm_logging.md +++ b/docs/reference/ocm_logging.md @@ -21,6 +21,7 @@ The following *realms* are used by the command line tool: - ocm/accessmethod/wget: access method for wget - ocm/blobaccess/wget: blob access for wget - ocm/compdesc: component descriptor handling + - ocm/compdesc/normalizations/legacy: component descriptor legacy normalization defaulting - ocm/config: configuration management - ocm/context: context lifecycle - ocm/credentials: Credentials diff --git a/docs/reference/ocm_sign_componentversions.md b/docs/reference/ocm_sign_componentversions.md index b1d3f582bf..fa9a0d7d17 100644 --- a/docs/reference/ocm_sign_componentversions.md +++ b/docs/reference/ocm_sign_componentversions.md @@ -25,7 +25,7 @@ componentversions, componentversion, cv, components, component, comps, comp, c --keyless use keyless signing --latest restrict component versions to latest --lookup stringArray repository name or spec for closure lookup fallback - -N, --normalization string normalization algorithm (default "jsonNormalisation/v1") + -N, --normalization string normalization algorithm (default "jsonNormalisation/v3") -K, --private-key stringArray private key setting -k, --public-key stringArray public key setting -R, --recursive recursively sign component versions @@ -124,8 +124,9 @@ The following signing types are supported with option --algorithm: The following normalization modes are supported with option --normalization: - - jsonNormalisation/v1 (default) + - jsonNormalisation/v1 - jsonNormalisation/v2 + - jsonNormalisation/v3 (default) The following hash modes are supported with option --hash: