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
: