Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package apko for openSUSE:Factory checked in at 2025-01-23 18:03:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/apko (Old) and /work/SRC/openSUSE:Factory/.apko.new.5589 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "apko" Thu Jan 23 18:03:32 2025 rev:34 rq:1239726 version:0.23.0 Changes: -------- --- /work/SRC/openSUSE:Factory/apko/apko.changes 2025-01-22 16:32:22.572081132 +0100 +++ /work/SRC/openSUSE:Factory/.apko.new.5589/apko.changes 2025-01-23 18:04:25.965706803 +0100 @@ -1,0 +2,6 @@ +Thu Jan 23 06:07:29 UTC 2025 - opensuse_buildserv...@ojkastl.de + +- Update to version 0.23.0: + * fix multi key support in APKINDEX verification (#1490) + +------------------------------------------------------------------- Old: ---- apko-0.22.7.obscpio New: ---- apko-0.23.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ apko.spec ++++++ --- /var/tmp/diff_new_pack.zDLyzk/_old 2025-01-23 18:04:31.145920622 +0100 +++ /var/tmp/diff_new_pack.zDLyzk/_new 2025-01-23 18:04:31.145920622 +0100 @@ -17,7 +17,7 @@ Name: apko -Version: 0.22.7 +Version: 0.23.0 Release: 0 Summary: Build OCI images from APK packages directly without Dockerfile License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.zDLyzk/_old 2025-01-23 18:04:31.181922109 +0100 +++ /var/tmp/diff_new_pack.zDLyzk/_new 2025-01-23 18:04:31.185922274 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/chainguard-dev/apko</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.22.7</param> + <param name="revision">v0.23.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.zDLyzk/_old 2025-01-23 18:04:31.205923099 +0100 +++ /var/tmp/diff_new_pack.zDLyzk/_new 2025-01-23 18:04:31.209923265 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/chainguard-dev/apko</param> - <param name="changesrevision">222193882ef17fda2b4763fb3970344c68fe666b</param></service></servicedata> + <param name="changesrevision">ec48e3070da9b4691b74de219fffd69d31da3f5d</param></service></servicedata> (No newline at EOF) ++++++ apko-0.22.7.obscpio -> apko-0.23.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.22.7/pkg/apk/apk/apkindex_test.go new/apko-0.23.0/pkg/apk/apk/apkindex_test.go --- old/apko-0.22.7/pkg/apk/apk/apkindex_test.go 2025-01-22 01:02:58.000000000 +0100 +++ new/apko-0.23.0/pkg/apk/apk/apkindex_test.go 2025-01-23 00:02:19.000000000 +0100 @@ -3,6 +3,7 @@ import ( "archive/tar" "compress/gzip" + "context" "fmt" "io" "os" @@ -238,3 +239,52 @@ require.Len(t, pkg.Provides, 0, "Expected no provides") require.Len(t, pkg.Dependencies, 0, "Expected no dependencies") } + +func TestMultipleKeys(t *testing.T) { + assert := assert.New(t) + // read all the keys from testdata/signing/keys + folder := "testdata/signing/keys" + // get all the files in the folder + files, _ := os.ReadDir(folder) + keys := make(map[string][]byte) + for _, file := range files { + if file.IsDir() { + continue + } + // read the file + keyFile, err := os.Open(fmt.Sprintf("%s/%s", folder, file.Name())) + require.Nil(t, err) + // parse the key + key, err := os.ReadFile(keyFile.Name()) + require.Nil(t, err) + keys[file.Name()] = key + } + // read the index file into []byte + indexBytes, err := os.ReadFile("testdata/signing/APKINDEX.tar.gz") + require.Nil(t, err) + + ctx := context.Background() + // There are 2^N-1 combinations of keys, where N is the number of keys + // We will test all of them + for comb := 1; comb < (1 << len(keys)); comb++ { + // get the keys to use + usedKeys := make(map[string][]byte) + for i := 0; i < len(keys); i++ { + if (comb & (1 << i)) != 0 { + usedKeys[files[i].Name()] = keys[files[i].Name()] + } + } + // parse the index + apkIndex, err := parseRepositoryIndex(ctx, "testdata/signing/APKINDEX.tar.gz", + usedKeys, "aarch64", indexBytes, &indexOpts{}) + require.Nil(t, err) + assert.Greater(len(apkIndex.Signature), 0, "Signature missing") + } + // Now, test the case where we have no matching key + _, err = parseRepositoryIndex(ctx, "testdata/signing/APKINDEX.tar.gz", + map[string][]byte{ + "unused-key": []byte("unused-key-data"), + }, + "aarch64", indexBytes, &indexOpts{}) + require.NotNil(t, err) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.22.7/pkg/apk/apk/index.go new/apko-0.23.0/pkg/apk/apk/index.go --- old/apko-0.22.7/pkg/apk/apk/index.go 2025-01-22 01:02:58.000000000 +0100 +++ new/apko-0.23.0/pkg/apk/apk/index.go 2025-01-23 00:02:19.000000000 +0100 @@ -44,6 +44,12 @@ var signatureFileRegex = regexp.MustCompile(`^\.SIGN\.(DSA|RSA|RSA256|RSA512)\.(.*\.rsa\.pub)$`) +type Signature struct { + KeyID string + Signature []byte + DigestAlgorithm crypto.Hash +} + // This is terrible but simpler than plumbing around a cache for now. // We just hold the parsed index in memory rather than re-parsing it every time, // which requires gunzipping, which is (somewhat) expensive. @@ -332,9 +338,11 @@ func parseRepositoryIndex(ctx context.Context, u string, keys map[string][]byte, arch string, b []byte, opts *indexOpts) (*APKIndex, error) { //nolint:gocyclo _, span := otel.Tracer("go-apk").Start(ctx, "parseRepositoryIndex") defer span.End() - // validate the signature if shouldCheckSignatureForIndex(u, arch, opts) { + if len(keys) == 0 { + return nil, fmt.Errorf("no keys provided to verify signature") + } buf := bytes.NewReader(b) gzipReader, err := gzip.NewReader(buf) if err != nil { @@ -348,14 +356,12 @@ tarReader := tar.NewReader(gzipReader) - var keyfile string - var signature []byte - var indexDigestType crypto.Hash + sigs := make([]Signature, 0, len(keys)) for { // read the signature(s) signatureFile, err := tarReader.Next() // found everything, end of stream - if len(keyfile) > 0 && errors.Is(err, io.EOF) { + if errors.Is(err, io.EOF) { break } // oops something went wrong @@ -366,29 +372,40 @@ if len(matches) != 3 { return nil, fmt.Errorf("failed to find key name in signature file name: %s", signatureFile.Name) } - // It is lucky that golang only iterates over sorted file names, and that - // lexically latest is the strongest hash + keyfile := matches[2] + if _, ok := keys[keyfile]; !ok { + // Ignore this signature if we don't have the key + continue + } + var digestAlgorithm crypto.Hash switch signatureType := matches[1]; signatureType { case "DSA": // Obsolete continue case "RSA": // Current legacy compat - indexDigestType = crypto.SHA1 + digestAlgorithm = crypto.SHA1 case "RSA256": // Current best practice - indexDigestType = crypto.SHA256 + digestAlgorithm = crypto.SHA256 case "RSA512": // Too big, too slow, not compiled in continue default: return nil, fmt.Errorf("unknown signature format: %s", signatureType) } - keyfile = matches[2] - signature, err = io.ReadAll(tarReader) + signature, err := io.ReadAll(tarReader) if err != nil { return nil, fmt.Errorf("failed to read signature from repository index: %w", err) } + sigs = append(sigs, Signature{ + KeyID: keyfile, + Signature: signature, + DigestAlgorithm: digestAlgorithm, + }) + } + if len(sigs) == 0 { + return nil, fmt.Errorf("no signature with known key found in repository index") } // we now have the signature bytes and name, get the contents of the rest; // this should be everything else in the raw gzip file as is. @@ -396,31 +413,26 @@ unreadBytes := buf.Len() readBytes := allBytes - unreadBytes indexData := b[readBytes:] - indexDigest, err := sign.HashData(indexData, indexDigestType) - if err != nil { - return nil, err - } - // now we can check the signature - if keys == nil { - return nil, fmt.Errorf("no keys provided to verify signature") - } - var verified bool - keyData, ok := keys[keyfile] - if ok { - if err := sign.RSAVerifyDigest(indexDigest, indexDigestType, signature, keyData); err != nil { - verified = false - } - } - if !verified { - for _, keyData := range keys { - if err := sign.RSAVerifyDigest(indexDigest, indexDigestType, signature, keyData); err == nil { - verified = true - break + indexDigest := make(map[crypto.Hash][]byte, len(keys)) + verified := false + for _, sig := range sigs { + // compute the digest if not already done + if _, hasDigest := indexDigest[sig.DigestAlgorithm]; !hasDigest { + digest, err := sign.HashData(indexData, sig.DigestAlgorithm) + if err != nil { + return nil, fmt.Errorf("failed to compute digest: %w", err) } + indexDigest[sig.DigestAlgorithm] = digest + } + if err := sign.RSAVerifyDigest(indexDigest[sig.DigestAlgorithm], sig.DigestAlgorithm, sig.Signature, keys[sig.KeyID]); err == nil { + verified = true + break + } else { + clog.FromContext(ctx).Warnf("failed to verify signature for keyfile %s: %v", sig.KeyID, err) } } if !verified { - return nil, fmt.Errorf("no key found to verify signature for keyfile %s; tried all other keys as well", keyfile) + return nil, errors.New("signature verification failed for repository index, for all provided keys") } } // with a valid signature, convert it to an ApkIndex Binary files old/apko-0.22.7/pkg/apk/apk/testdata/signing/APKINDEX.tar.gz and new/apko-0.23.0/pkg/apk/apk/testdata/signing/APKINDEX.tar.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-0106f58bac88057c2ff5c2829850df492717a876ed700443550353c7ab23f5a0.rsa.pub new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-0106f58bac88057c2ff5c2829850df492717a876ed700443550353c7ab23f5a0.rsa.pub --- old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-0106f58bac88057c2ff5c2829850df492717a876ed700443550353c7ab23f5a0.rsa.pub 1970-01-01 01:00:00.000000000 +0100 +++ new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-0106f58bac88057c2ff5c2829850df492717a876ed700443550353c7ab23f5a0.rsa.pub 2025-01-23 00:02:19.000000000 +0100 @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2TxPZPC1Cdb647P0kvi5 +wtwcoaFCOGHspL3RmmD2idG2FNZc/rpwyIp2bnprFvOCWm7bKAEl/9JDfAGm7DAP +q/EC7UeJ9yx03jY1I7+Oj1/1UUIyuvfDwyiMxEwcABaivAn9WhYZ5YeZKK8pUYoX +hOPNqgI+N3BY1H+hgg3xgTrzWmfRSsvTEhSqoLHdl4qbsM5EE/T5W7u+96y9J2vn +M89rUP557K3n1QKGzTIGjlesFQd8y+uH0TdAe6AFmxFpftdXRu4KInjdrDOArfBO +cKLB0gHOERkd08JVMWYHd6Ne7BfWpwxmge371GgTj8XYOJrkAj3cg8ked/51Maw0 +FLt4aErQR6y02QIluGZ4gwbIZ8y+iBWvoh8egiab3Whr3SwGuX6X8WJbOgKLCyGK +CmrxgPO/ahS4XUWtuTp3woIZBnnHGOBdHjRARkl9PdBgN2HhZPS9vj8rrIqbTp3c +dmqhcsjsgmss6Sb3rOcOL3N3V8+dMiD7VZXeIgVocOpRtLJGQFCupjAfmz5ub4t5 +lBI2EgbR9LdSg9mrFoYbG11sU6MLa0iPMA8gU3nG4dzLDpsF9PbXy6sfBXXc8W25 +tfwZnV4wHouWEhiQ9Hfc4NOGcs+01Zwg9CZRS2SBKC9iqUPsz3RaTAKWsS/QNTND +Zy9znhL5yGrbHaq2BOlnVqMCAwEAAQ== +-----END PUBLIC KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-60912bbc46bfc8ed6bda0b50db3a8a5f3c4344d6bc8549ec9b84d96140b475d1.rsa.pub new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-60912bbc46bfc8ed6bda0b50db3a8a5f3c4344d6bc8549ec9b84d96140b475d1.rsa.pub --- old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-60912bbc46bfc8ed6bda0b50db3a8a5f3c4344d6bc8549ec9b84d96140b475d1.rsa.pub 1970-01-01 01:00:00.000000000 +0100 +++ new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-60912bbc46bfc8ed6bda0b50db3a8a5f3c4344d6bc8549ec9b84d96140b475d1.rsa.pub 2025-01-23 00:02:19.000000000 +0100 @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAizNP/QKfB5NKlVBba6dX +Jzz0/icPE++eM3aAtCARwlAKyRm3ivWM5sAz88lwHFjMAQV0QPD3hDgswcAExTXa +EPN8M0RroxxnWuJLGhz99ONaGOLo66GDkcdBaaZIinXoWmhqg1zcequ6dLDOGArm +ZR99egaBFKyQHvBYHIl5dQ1yT8us/m1Yfb7DlERcU2lf3lFhLDrJsBgVw5QYa5h+ +aSneA/B2ZB1BwfyE3F5+UUn0qWRy3Yg526oaWpjjwCaPVq4yaJfg4XuMS82wdVm9 +G6awP1PE0EWwgkcc8z8w7BmxFhdCRt0VNjmept14tJwiU0d81j1HPHsIkqWr2TBA +f7Wwp34+ByfA7iHbKXFPgK1MKmNzyANoyPaPRo+x+MU8D2AqUBslKOKrhckyd4tQ +ch3SyaMKMa7hc0PWnFrCCtDTmNZQaquNrUWnbbMtdkjCA/is//W+Lqn+O6BjmR9U +yztR26l2OwGMu6uf3CMUADX/ra4k2BuxSnaPTgm7M19AAzXSnICa+aupgFsIfx1w +UyGlq8dvCz8bnipXW4EbTWTSPdFrxQl/LQUB5w82kMorML1TQEpSLjoSmnhNg+rh +rqIsjp7fwEnX/qTbiwUhgcN1fyLkGGv/UV/H62WcclStKiRHp+ND0ePMABefA+cv +f9tYynbOV78qct+dQKkQ17kCAwEAAQ== +-----END PUBLIC KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-61cb6bccd1f584b007db7be51ebce2b0530a54cc5a94e7650b570d113d537cf3.rsa.pub new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-61cb6bccd1f584b007db7be51ebce2b0530a54cc5a94e7650b570d113d537cf3.rsa.pub --- old/apko-0.22.7/pkg/apk/apk/testdata/signing/keys/chainguard-61cb6bccd1f584b007db7be51ebce2b0530a54cc5a94e7650b570d113d537cf3.rsa.pub 1970-01-01 01:00:00.000000000 +0100 +++ new/apko-0.23.0/pkg/apk/apk/testdata/signing/keys/chainguard-61cb6bccd1f584b007db7be51ebce2b0530a54cc5a94e7650b570d113d537cf3.rsa.pub 2025-01-23 00:02:19.000000000 +0100 @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwlV2kG9xGtkVWc/dS9nC +Qqef/hx0AysYfrMFetY+mZ0MPvELqOaDJgtjlbMM6w/ujqDtScxbnNNf5/vY/wBh +rHTeKU+oDlkRTsh8ZxKINFfGGSDxR2pKkvPZeVzxdhTNEPK9ZggbFHe5RhXUT9iK +CNgF+Xa3p5A1Yi8zR6zEFJ5EDBMnTMagV2ueLfJrpFMm/4hBn6zWtjyDhDPVZyTD +DyQc2/FIDXM1tJONWDT8wO2xuHf7xKGr6lO/CZca5Pnd/QS5jODoNbTo9VvG+oQt +mRsWsdVFst0vS54s3mchtuAB2NXnWAZZuux9uYPgvO8eqI0RLqCKX7VLJ8tGOTmL +3dblTzKbKbVcy4uHsyH2mZPvPUTO+ck7c58iSeUrOFV50B6zqynAXgCrDo3XMjpI +mLC26LRMqF/Q/RWc9R0K+ZgiLQ7ootyOJILPSR69RhgdQJVt7/gz55dM89yoq/B9 +U2V+1mv0rBQmmNgCM1U3QdgUv5WeIm4Ed2avKZBwJLBVR5AxlY9xlwJ7sEU4Ms2t +cEblWhqDze/sSsjCYOAmD4/M/90OnBelg+/BU2jOD4nqQdEQDOZdo+EUVeCIuPwD +kz8kTv8pPtLjKI0jRhYLn2dhg/vTIDJf19iXexGQXOyK/rNwi41i+9snypVb8YSe +cWTnq+m2CFOiXFsDWQh7RsUCAwEAAQ== +-----END PUBLIC KEY----- ++++++ apko.obsinfo ++++++ --- /var/tmp/diff_new_pack.zDLyzk/_old 2025-01-23 18:04:31.497935153 +0100 +++ /var/tmp/diff_new_pack.zDLyzk/_new 2025-01-23 18:04:31.497935153 +0100 @@ -1,5 +1,5 @@ name: apko -version: 0.22.7 -mtime: 1737504178 -commit: 222193882ef17fda2b4763fb3970344c68fe666b +version: 0.23.0 +mtime: 1737586939 +commit: ec48e3070da9b4691b74de219fffd69d31da3f5d ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/apko/vendor.tar.gz /work/SRC/openSUSE:Factory/.apko.new.5589/vendor.tar.gz differ: char 5, line 1