From 830edf88770482a58d7741473f553187a528e614 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Tue, 17 Dec 2024 18:22:04 +0200 Subject: [PATCH] Use the correct verifier for RSA PSS scheme keys (#625) * Explicitly use LoadRSAPSSVerifier for RSASSA_PSS_SHA256 keys Signed-off-by: Radoslav Dimitrov * Update the basic_repository.go example to work with RSA PSS key scheme Signed-off-by: Radoslav Dimitrov * added a simple tool that can resign test metadata and updated signatures to be pss and not pkcs1 v1.5 Signed-off-by: Fredrik Skogman * Fixed failing unit tests Signed-off-by: Fredrik Skogman --------- Signed-off-by: Radoslav Dimitrov Signed-off-by: Fredrik Skogman Co-authored-by: Fredrik Skogman --- examples/repository/basic_repository.go | 6 +- .../repository/metadata/root.json | 138 ++++++------ .../repository/metadata/snapshot.json | 46 ++-- .../repository/metadata/targets.json | 96 ++++----- .../repository/metadata/timestamp.json | 34 +-- internal/testutils/rsapss/signer.go | 35 +++ internal/testutils/signer/signer.go | 203 ++++++++++++++++++ metadata/metadata.go | 17 +- metadata/metadata_api_test.go | 47 ++-- .../trustedmetadata/trustedmetadata_test.go | 59 +++-- 10 files changed, 475 insertions(+), 206 deletions(-) create mode 100644 internal/testutils/rsapss/signer.go create mode 100644 internal/testutils/signer/signer.go diff --git a/examples/repository/basic_repository.go b/examples/repository/basic_repository.go index e8942b72..479a957d 100644 --- a/examples/repository/basic_repository.go +++ b/examples/repository/basic_repository.go @@ -499,7 +499,9 @@ func main() { // Use a mixture of key types // ========================== - // Create an RSA key + // Create an RSA key. + // Note TUF should use an RSA PSS key scheme, not RSA PKCS1v15. + // Reference: https://theupdateframework.github.io/specification/latest/#file-formats-keys anotherRootKeyRSA, _ := rsa.GenerateKey(rand.Reader, 2048) if err != nil { panic(fmt.Sprintln("basic_repository.go:", "RSA key generation failed", err)) @@ -549,7 +551,7 @@ func main() { } // Sign root with the new RSA and ECDSA keys - outofbandSignerRSA, err := signature.LoadSigner(anotherRootKeyRSA, crypto.SHA256) + outofbandSignerRSA, err := signature.LoadRSAPSSSigner(anotherRootKeyRSA, crypto.SHA256, &rsa.PSSOptions{Hash: crypto.SHA256}) if err != nil { panic(fmt.Sprintln("basic_repository.go:", "loading RSA signer failed", err)) } diff --git a/internal/testutils/repository_data/repository/metadata/root.json b/internal/testutils/repository_data/repository/metadata/root.json index 584a3ec9..6acbb83f 100644 --- a/internal/testutils/repository_data/repository/metadata/root.json +++ b/internal/testutils/repository_data/repository/metadata/root.json @@ -1,71 +1,71 @@ { - "signatures": [ - { - "keyid": "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2", - "sig": "d0283ac0653e324ce132e47a518f8a1539b59430efe5cdec58ec53f824bec28628b57dd5fb2452bde83fc8f5d11ab0b7350a9bbcbefc7acc6c447785545fa1e36f1352c9e20dd1ebcc3ab16a2a7ff702e32e481ceba88e0f348dc2cddd26ca577445d00c7194e8656d901fd2382c479555af93a64eef48cf79cdff6ecdcd7cb7" - } - ], - "signed": { - "_type": "root", - "consistent_snapshot": true, - "expires": "2030-08-15T14:30:45.0000001Z", - "keys": { - "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946": { - "keytype": "rsa", - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHXjYnWGuCIOh5T3XGmgG/RsXWHP\nTbyu7OImP6O+uHg8hui8C1nY/mcJdFdxqgl1vKEco/Nwebh2T8L6XbNfcgV9VVst\nWpeCalZYWi55lZSLe9KixQIAyg15rNdhN9pcD3OuLmFvslgTx+dTbZ3ZoYMbcb4C\n5yqvqzcOoCTQMeWbAgMBAAE=\n-----END PUBLIC KEY-----\n" - }, - "scheme": "rsassa-pss-sha256" - }, - "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d": { - "keytype": "rsa", - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjm6HPktvTGsygQ8Gvmu+zydTN\ne1zqoxLxV7mVRbmsCI4kn7JTHc4fmWZwvo7f/Wbto6Xj5HqGJFSlYIGZuTwZqPg3\nw8wqv8cuPxbmsFSxMoHfzBBIuJe0FlwXFysojbdhrSUqNL84tlwTFXEhePYrpTNM\nDn+9T55B0WJYT/VPxwIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "scheme": "rsassa-pss-sha256" - }, - "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2": { - "keytype": "rsa", - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDydf/VEpxBOCDoxpM6IVhq9i67\nP9BiVv2zwZSUO/M0RTToAvFvNgDKXwtnp8LyjVk++wMA1aceMa+pS7vYrKvPIJa7\nWIT+mwy86/fIdnllJDMw5tmLr2mE3oBMxOhpEiD2tO+liGacklFNk6nHHorX9S91\niqpdRVa3zJw5ALvLdwIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "scheme": "rsassa-pss-sha256" - }, - "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6": { - "keytype": "rsa", - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPQoHresXRRRGoinN3bNn+BI23\nKolXdXLGqYkTvr9AjemUQJxbqmvZXHboQMAYw8OuBrRNt5Fz20wjsrJwOBEU5U3n\nHSJI4zYPGckYci0/0Eo2Kjws5BmIj38qgIfhsH4zyZ4FZZ+GLRn+W3i3wl6SfRMC\n/HCg0DDwi75faC0vGQIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "scheme": "rsassa-pss-sha256" - } - }, - "roles": { - "root": { - "keyids": [ - "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2" - ], - "threshold": 1 - }, - "snapshot": { - "keyids": [ - "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6" - ], - "threshold": 1 - }, - "targets": { - "keyids": [ - "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d" - ], - "threshold": 1 - }, - "timestamp": { - "keyids": [ - "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946" - ], - "threshold": 1 - } - }, - "spec_version": "1.0.31", - "version": 1 - } + "signatures": [ + { + "keyid": "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2", + "sig": "60d502a798f44577a76a1d7656a60099cd3a995d3d71a0a234aadbd16e38c14611920b8aef9ed78ca4ac0c02277cd72a6fc5ef484a3d66a6c70a61199e462681eb2e667046d4fbc2be1e50cf9fe00fda8fcd6534599eddc91716bf38e7fbbf375524fdb702c74076fd37dcd401d5263783150e851bdba9ef6f9c9a08adf6b289" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2030-08-15T14:30:45.0000001Z", + "keys": { + "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946": { + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHXjYnWGuCIOh5T3XGmgG/RsXWHP\nTbyu7OImP6O+uHg8hui8C1nY/mcJdFdxqgl1vKEco/Nwebh2T8L6XbNfcgV9VVst\nWpeCalZYWi55lZSLe9KixQIAyg15rNdhN9pcD3OuLmFvslgTx+dTbZ3ZoYMbcb4C\n5yqvqzcOoCTQMeWbAgMBAAE=\n-----END PUBLIC KEY-----\n" + }, + "scheme": "rsassa-pss-sha256" + }, + "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d": { + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjm6HPktvTGsygQ8Gvmu+zydTN\ne1zqoxLxV7mVRbmsCI4kn7JTHc4fmWZwvo7f/Wbto6Xj5HqGJFSlYIGZuTwZqPg3\nw8wqv8cuPxbmsFSxMoHfzBBIuJe0FlwXFysojbdhrSUqNL84tlwTFXEhePYrpTNM\nDn+9T55B0WJYT/VPxwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "scheme": "rsassa-pss-sha256" + }, + "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2": { + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDydf/VEpxBOCDoxpM6IVhq9i67\nP9BiVv2zwZSUO/M0RTToAvFvNgDKXwtnp8LyjVk++wMA1aceMa+pS7vYrKvPIJa7\nWIT+mwy86/fIdnllJDMw5tmLr2mE3oBMxOhpEiD2tO+liGacklFNk6nHHorX9S91\niqpdRVa3zJw5ALvLdwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "scheme": "rsassa-pss-sha256" + }, + "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6": { + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPQoHresXRRRGoinN3bNn+BI23\nKolXdXLGqYkTvr9AjemUQJxbqmvZXHboQMAYw8OuBrRNt5Fz20wjsrJwOBEU5U3n\nHSJI4zYPGckYci0/0Eo2Kjws5BmIj38qgIfhsH4zyZ4FZZ+GLRn+W3i3wl6SfRMC\n/HCg0DDwi75faC0vGQIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "scheme": "rsassa-pss-sha256" + } + }, + "roles": { + "root": { + "keyids": [ + "74b58be26a6ff00ab2eec9b14da29038591a69c212223033f4efdf24489913f2" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.31", + "version": 1 + } } \ No newline at end of file diff --git a/internal/testutils/repository_data/repository/metadata/snapshot.json b/internal/testutils/repository_data/repository/metadata/snapshot.json index ed379e01..24ec1517 100644 --- a/internal/testutils/repository_data/repository/metadata/snapshot.json +++ b/internal/testutils/repository_data/repository/metadata/snapshot.json @@ -1,25 +1,25 @@ { - "signatures": [ - { - "keyid": "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6", - "sig": "3075fe9ef3008603eb0531500a93101b8f7eb52b07ce63fb71abaffd5eb20784bcab888abfca8041798b13dd35c6e18ff4a64d536161c4d5e7535f006edec3a46c71684a632269222da82d50bf380e20eb477032e45df0b44af9e1dc46f25cd72f9901b4fc41b90869649b6257a66188b61b83c7295baf16f113e9cc4d39b3a6" - } - ], - "signed": { - "_type": "snapshot", - "expires": "2030-08-15T14:30:45.0000001Z", - "meta": { - "role1.json": { - "version": 1 - }, - "role2.json": { - "version": 1 - }, - "targets.json": { - "version": 1 - } - }, - "spec_version": "1.0.31", - "version": 1 - } + "signatures": [ + { + "keyid": "8a14f637b21578cc292a67899df0e46cc160d7fd56e9beae898adb666f4fd9d6", + "sig": "1fdc41488f58482d8af1dad681b9076d54c61b3f5e11bd6cf3c8102b2863471f82c0be2cccd978a9bb6afb43e07dd7e806028a883eaeafd32d5f5277d6419363ecb1475286a61996a4bb4b325b703d3bd60381227af0a3826f7f119a451086bcb5b13a525184d1b2a941ab9a270d2c9c8e584162c5857b138a38c33892e2a921" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2030-08-15T14:30:45.0000001Z", + "meta": { + "role1.json": { + "version": 1 + }, + "role2.json": { + "version": 1 + }, + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.31", + "version": 1 + } } \ No newline at end of file diff --git a/internal/testutils/repository_data/repository/metadata/targets.json b/internal/testutils/repository_data/repository/metadata/targets.json index 41399c90..1e06afa0 100644 --- a/internal/testutils/repository_data/repository/metadata/targets.json +++ b/internal/testutils/repository_data/repository/metadata/targets.json @@ -1,50 +1,50 @@ { - "signatures": [ - { - "keyid": "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d", - "sig": "80cd125a4b128c9508df8bc6f71ad2ed9896a9e7afccd53fca9e7dbc2f02db69c3ae712234d3730c929d891fa035bdf059736e7debf62cbac6f0e8d22ab0c5de3b3e47b249eb0d41dea66d9fda9588893cde824a95614129263b6fed72fafb21cd7114e603fe3a30e3871e9eb5b5029e3e9a8353190f1bcb332a81ec211a93eb" - } - ], - "signed": { - "_type": "targets", - "delegations": { - "keys": { - "c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ed25519", - "keyval": { - "public": "fcf224e55fa226056adf113ef1eb3d55e308b75b321c8c8316999d8c4fd9e0d9" - }, - "scheme": "ed25519" - } - }, - "roles": [ - { - "keyids": [ - "c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a" - ], - "name": "role1", - "paths": [ - "file3.txt" - ], - "terminating": false, - "threshold": 1 - } - ] - }, - "expires": "2030-08-15T14:30:45.0000001Z", - "spec_version": "1.0.31", - "targets": { - "file1.txt": { - "hashes": { - "sha256": "65b8c67f51c993d898250f40aa57a317d854900b3a04895464313e48785440da" - }, - "length": 31 - } - }, - "version": 1 - } + "signatures": [ + { + "keyid": "282612f348dcd7fe3f19e0f890e89fad48d45335deeb91deef92873934e6fe6d", + "sig": "699c0c762032f6471fceabfd2d61b80f352ad6bf2ba5fd7b114fd0b9b1ab8d94f1482776b3fef43c53183a9c9cd7f5de671cbdafdd5032bbe2c42273e953bf3ce9f99c2d46dac8802d6155082e10313e22c4886af2be113b626f2a8af930e01ed41df50a5dbe6ca4cedf5f5d2a7f3b7e7090abacc8aebd6e021ad021d3580cad" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "fcf224e55fa226056adf113ef1eb3d55e308b75b321c8c8316999d8c4fd9e0d9" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a" + ], + "name": "role1", + "paths": [ + "file3.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2030-08-15T14:30:45.0000001Z", + "spec_version": "1.0.31", + "targets": { + "file1.txt": { + "hashes": { + "sha256": "65b8c67f51c993d898250f40aa57a317d854900b3a04895464313e48785440da" + }, + "length": 31 + } + }, + "version": 1 + } } \ No newline at end of file diff --git a/internal/testutils/repository_data/repository/metadata/timestamp.json b/internal/testutils/repository_data/repository/metadata/timestamp.json index aa11c742..c361c552 100644 --- a/internal/testutils/repository_data/repository/metadata/timestamp.json +++ b/internal/testutils/repository_data/repository/metadata/timestamp.json @@ -1,19 +1,19 @@ { - "signatures": [ - { - "keyid": "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946", - "sig": "639c9ce3dbb705265b5e9ad6d67fea2b38780c48ff7917e372adace8e50a7a2f054383d5960457a113059be521b8ce7e6d8a5787c600c4850b8c0ed1ae17a931a6bfe794476e7824c6f53df5232561e0a2e146b11dde7889b397c6f8136e2105bbb21b4b59b5addc032a0e755d97e531255f3b458d474184168541e542626e81" - } - ], - "signed": { - "_type": "timestamp", - "expires": "2030-08-15T14:30:45.0000001Z", - "meta": { - "snapshot.json": { - "version": 1 - } - }, - "spec_version": "1.0.31", - "version": 1 - } + "signatures": [ + { + "keyid": "142919f8e933d7045abff3be450070057814da36331d7a22ccade8b35a9e3946", + "sig": "043b312da9ee1444e3ef539d943891b2690a8d75624c0f05ec148790fd698a7eb1501428167872794857e9669a451619cc796658782b1d46ecc59d1aca0db7233416e81074ef4f54fd845ad8e4216b4cd5163d815be9ecbf73f34aacd25b60c99da88cf641ba5715c37f34a6bc036061c05a42066f554714ee8647c47ae5c16e" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2030-08-15T14:30:45.0000001Z", + "meta": { + "snapshot.json": { + "version": 1 + } + }, + "spec_version": "1.0.31", + "version": 1 + } } \ No newline at end of file diff --git a/internal/testutils/rsapss/signer.go b/internal/testutils/rsapss/signer.go new file mode 100644 index 00000000..9b0c1656 --- /dev/null +++ b/internal/testutils/rsapss/signer.go @@ -0,0 +1,35 @@ +package rsapss + +import ( + "crypto" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "os" + + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/options" +) + +func LoadRSAPSSSignerFromPEMFile(p string) (signature.Signer, error) { + var b []byte + var block *pem.Block + var pk any + var err error + + if b, err = os.ReadFile(p); err != nil { + return nil, err + } + + if block, _ = pem.Decode(b); len(block.Bytes) == 0 { + return nil, errors.New("empty PEM block") + } + + if pk, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { + return nil, err + } + var pssOpt = rsa.PSSOptions{Hash: crypto.SHA256} + + return signature.LoadSignerWithOpts(pk, options.WithRSAPSS(&pssOpt)) +} diff --git a/internal/testutils/signer/signer.go b/internal/testutils/signer/signer.go new file mode 100644 index 00000000..5d893f4c --- /dev/null +++ b/internal/testutils/signer/signer.go @@ -0,0 +1,203 @@ +// Copyright 2024 The Update Framework Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "crypto" + "crypto/rsa" + "crypto/x509" + "encoding/json" + "encoding/pem" + "errors" + "flag" + "fmt" + "os" + + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/options" + "github.com/theupdateframework/go-tuf/v2/metadata" +) + +type tufSigner interface { + Sign(s signature.Signer) (*metadata.Signature, error) +} + +/* + Run this to sign test data, like this: + ~/git/go-tuf/internal/testutils $ go run \ + signer/signer.go \ + -k repository_data/keystore/timestamp_key \ + -s rsassa-pss-sha256 \ + -f repository_data/repository/metadata/timestamp.json +*/ + +func main() { + var scheme = flag.String("s", "", "set scheme to use for key") + var key = flag.String("k", "", "key file to load") + var f = flag.String("f", "", "file to sign") + + flag.Parse() + + if *scheme == "" { + fmt.Println("no scheme is set") + os.Exit(1) + } + if *key == "" { + fmt.Println("no key provided") + os.Exit(1) + } + if *f == "" { + fmt.Println("no metadata file provided") + os.Exit(1) + } + + t, err := getTUFMDRole(*f) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + s, err := loadSigner(*key, *scheme) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + var ts tufSigner + + switch t { + case metadata.ROOT: + var rmd metadata.Metadata[metadata.RootType] + if _, err = rmd.FromFile(*f); err != nil { + fmt.Println(err) + os.Exit(1) + } + rmd.Signatures = []metadata.Signature{} + ts = &rmd + case metadata.TARGETS: + var tmd metadata.Metadata[metadata.TargetsType] + if _, err = tmd.FromFile(*f); err != nil { + fmt.Println(err) + os.Exit(1) + } + tmd.Signatures = []metadata.Signature{} + ts = &tmd + case metadata.SNAPSHOT: + var smd metadata.Metadata[metadata.SnapshotType] + if _, err = smd.FromFile(*f); err != nil { + fmt.Println(err) + os.Exit(1) + } + smd.Signatures = []metadata.Signature{} + ts = &smd + case metadata.TIMESTAMP: + var tsmd metadata.Metadata[metadata.TimestampType] + if _, err = tsmd.FromFile(*f); err != nil { + fmt.Println(err) + os.Exit(1) + } + tsmd.Signatures = []metadata.Signature{} + ts = &tsmd + } + + if _, err = ts.Sign(s); err != nil { + fmt.Printf("failed to sign metadata of type %s: %s\n", + t, err) + os.Exit(1) + } + + if err = persist(*f, ts); err != nil { + fmt.Printf("failed to persist updated metadata %s: %s\n", + *f, err) + } +} + +func persist(p string, md any) error { + jsonBytes, err := json.MarshalIndent(md, "", " ") + if err != nil { + return err + } + + err = os.WriteFile(p, jsonBytes, 0600) + return err +} + +func loadSigner(k, s string) (signature.Signer, error) { + var pk any + var err error + var opts []signature.LoadOption + var rawKey []byte + + switch s { + case metadata.KeySchemeRSASSA_PSS_SHA256: + if rawKey, err = getPemBytes(k); err != nil { + return nil, err + } + if pk, err = x509.ParsePKCS1PrivateKey(rawKey); err != nil { + return nil, err + } + var pssOpt = rsa.PSSOptions{Hash: crypto.SHA256} + opts = append(opts, options.WithRSAPSS(&pssOpt)) + default: + return nil, fmt.Errorf("unsupported key scheme %s\n", s) + } + + return signature.LoadSignerWithOpts(pk, opts...) +} + +func getPemBytes(p string) ([]byte, error) { + var b []byte + var block *pem.Block + var err error + + if b, err = os.ReadFile(p); err != nil { + return nil, err + } + + if block, _ = pem.Decode(b); len(block.Bytes) == 0 { + return nil, errors.New("empty PEM block") + } + + return block.Bytes, nil +} + +func getTUFMDRole(p string) (string, error) { + var m map[string]any + + mdBytes, err := os.ReadFile(p) + if err != nil { + return "", fmt.Errorf("failed to read file %s: %w", p, err) + } + + if err := json.Unmarshal(mdBytes, &m); err != nil { + return "", fmt.Errorf("failed to parse TUF metadata: %w", err) + } + signedType := m["signed"].(map[string]any)["_type"].(string) + switch signedType { + case metadata.ROOT: + fallthrough + case metadata.TARGETS: + fallthrough + case metadata.SNAPSHOT: + fallthrough + case metadata.TIMESTAMP: + return signedType, nil + default: + return "", fmt.Errorf("unsupported role '%s'", signedType) + } +} diff --git a/metadata/metadata.go b/metadata/metadata.go index 3e0a9e1a..f71c08fe 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -21,6 +21,7 @@ import ( "bytes" "crypto" "crypto/hmac" + "crypto/rsa" "crypto/sha256" "crypto/sha512" "encoding/base64" @@ -322,7 +323,21 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata } } // load a verifier based on that key - verifier, err := signature.LoadVerifier(publicKey, hash) + // handle RSA PSS scheme separately as the LoadVerifier function doesn't identify it correctly + // Note we should support RSA PSS, not RSA PKCS1v15 (which is what LoadVerifier would return) + // Reference: https://theupdateframework.github.io/specification/latest/#file-formats-keys + var verifier signature.Verifier + if key.Type == KeyTypeRSASSA_PSS_SHA256 { + // Load a verifier for rsa + publicKeyRSAPSS, ok := publicKey.(*rsa.PublicKey) + if !ok { + return &ErrType{Msg: "failed to convert public key to RSA PSS key"} + } + verifier, err = signature.LoadRSAPSSVerifier(publicKeyRSAPSS, hash, &rsa.PSSOptions{Hash: crypto.SHA256}) + } else { + // Load a verifier for ed25519 and ecdsa + verifier, err = signature.LoadVerifier(publicKey, hash) + } if err != nil { return err } diff --git a/metadata/metadata_api_test.go b/metadata/metadata_api_test.go index 4b3f781f..5ae503d6 100644 --- a/metadata/metadata_api_test.go +++ b/metadata/metadata_api_test.go @@ -20,6 +20,7 @@ package metadata import ( "bytes" "crypto" + "crypto/rsa" "crypto/sha256" "encoding/json" "fmt" @@ -35,6 +36,7 @@ import ( "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" "github.com/theupdateframework/go-tuf/v2/internal/testutils" + "github.com/theupdateframework/go-tuf/v2/internal/testutils/rsapss" ) func TestMain(m *testing.M) { @@ -147,7 +149,7 @@ func TestCompareFromBytesFromFileToBytes(t *testing.T) { assert.NoError(t, err) rootBytesActual, err := root.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, rootBytesWant, rootBytesActual) + assert.Equal(t, stripWhitespaces(rootBytesWant), stripWhitespaces(rootBytesActual)) targetsPath := filepath.Join(testutils.RepoDir, "targets.json") targetsBytesWant, err := os.ReadFile(targetsPath) @@ -156,7 +158,7 @@ func TestCompareFromBytesFromFileToBytes(t *testing.T) { assert.NoError(t, err) targetsBytesActual, err := targets.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, targetsBytesWant, targetsBytesActual) + assert.Equal(t, stripWhitespaces(targetsBytesWant), stripWhitespaces(targetsBytesActual)) snapshotPath := filepath.Join(testutils.RepoDir, "snapshot.json") snapshotBytesWant, err := os.ReadFile(snapshotPath) @@ -165,7 +167,7 @@ func TestCompareFromBytesFromFileToBytes(t *testing.T) { assert.NoError(t, err) snapshotBytesActual, err := snapshot.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, snapshotBytesWant, snapshotBytesActual) + assert.Equal(t, stripWhitespaces(snapshotBytesWant), stripWhitespaces(snapshotBytesActual)) timestampPath := filepath.Join(testutils.RepoDir, "timestamp.json") timestampBytesWant, err := os.ReadFile(timestampPath) @@ -174,7 +176,7 @@ func TestCompareFromBytesFromFileToBytes(t *testing.T) { assert.NoError(t, err) timestampBytesActual, err := timestamp.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, timestampBytesWant, timestampBytesActual) + assert.Equal(t, stripWhitespaces(timestampBytesWant), stripWhitespaces(timestampBytesActual)) } func TestRootReadWriteReadCompare(t *testing.T) { @@ -265,6 +267,11 @@ func TestTimestampReadWriteReadCompare(t *testing.T) { assert.NoError(t, err) } +func stripWhitespaces(b []byte) []byte { + tmp := strings.ReplaceAll(string(b), " ", "") + return []byte(strings.ReplaceAll(tmp, "\t", "")) +} + func TestToFromBytes(t *testing.T) { // ROOT rootPath := filepath.Join(testutils.RepoDir, "root.json") @@ -278,15 +285,16 @@ func TestToFromBytes(t *testing.T) { // Case 1: test noncompact by overriding the default serializer. rootBytesWant, err := root.ToBytes(true) + assert.NoError(t, err) - assert.Equal(t, data, rootBytesWant) + assert.Equal(t, stripWhitespaces(rootBytesWant), stripWhitespaces(data)) // Case 2: test compact by using the default serializer. root2, err := Root().FromBytes(rootBytesWant) assert.NoError(t, err) rootBytesActual, err := root2.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, rootBytesWant, rootBytesActual) + assert.Equal(t, stripWhitespaces(rootBytesWant), stripWhitespaces(rootBytesActual)) // SNAPSHOT data, err = os.ReadFile(filepath.Join(testutils.RepoDir, "snapshot.json")) @@ -297,14 +305,14 @@ func TestToFromBytes(t *testing.T) { // Case 1: test noncompact by overriding the default serializer. snapshotBytesWant, err := snapshot.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, data, snapshotBytesWant) + assert.Equal(t, stripWhitespaces(data), stripWhitespaces(snapshotBytesWant)) // Case 2: test compact by using the default serializer. snapshot2, err := Snapshot().FromBytes(snapshotBytesWant) assert.NoError(t, err) snapshotBytesActual, err := snapshot2.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, snapshotBytesWant, snapshotBytesActual) + assert.Equal(t, stripWhitespaces(snapshotBytesWant), stripWhitespaces(snapshotBytesActual)) // TARGETS data, err = os.ReadFile(filepath.Join(testutils.RepoDir, "targets.json")) @@ -315,14 +323,14 @@ func TestToFromBytes(t *testing.T) { // Case 1: test noncompact by overriding the default serializer. targetsBytesWant, err := targets.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, data, targetsBytesWant) + assert.Equal(t, stripWhitespaces(data), stripWhitespaces(targetsBytesWant)) // Case 2: test compact by using the default serializer. targets2, err := Targets().FromBytes(targetsBytesWant) assert.NoError(t, err) targetsBytesActual, err := targets2.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, targetsBytesWant, targetsBytesActual) + assert.Equal(t, stripWhitespaces(targetsBytesWant), stripWhitespaces(targetsBytesActual)) // TIMESTAMP data, err = os.ReadFile(filepath.Join(testutils.RepoDir, "timestamp.json")) @@ -333,15 +341,14 @@ func TestToFromBytes(t *testing.T) { // Case 1: test noncompact by overriding the default serializer. timestampBytesWant, err := timestamp.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, data, timestampBytesWant) + assert.Equal(t, stripWhitespaces(data), stripWhitespaces(timestampBytesWant)) // Case 2: test compact by using the default serializer. timestamp2, err := Timestamp().FromBytes(timestampBytesWant) assert.NoError(t, err) timestampBytesActual, err := timestamp2.ToBytes(true) assert.NoError(t, err) - assert.Equal(t, timestampBytesWant, timestampBytesActual) - + assert.Equal(t, stripWhitespaces(timestampBytesWant), stripWhitespaces(timestampBytesActual)) } func TestSignVerify(t *testing.T) { @@ -371,7 +378,11 @@ func TestSignVerify(t *testing.T) { targetsPublicKey, err := targetsKey.ToPublicKey() assert.NoError(t, err) targetsHash := crypto.SHA256 - targetsVerifier, err := signature.LoadVerifier(targetsPublicKey, targetsHash) + targetsVerifier, err := signature.LoadRSAPSSVerifier( + targetsPublicKey.(*rsa.PublicKey), + targetsHash, + &rsa.PSSOptions{Hash: targetsHash}, + ) assert.NoError(t, err) err = targetsVerifier.VerifySignature(bytes.NewReader(sig), bytes.NewReader(data)) assert.NoError(t, err) @@ -459,7 +470,11 @@ func TestKeyVerifyFailures(t *testing.T) { timestampPublicKey, err = timestampKey.ToPublicKey() assert.NoError(t, err) timestampHash = crypto.SHA256 - timestampVerifier, err = signature.LoadVerifier(timestampPublicKey, timestampHash) + timestampVerifier, err = signature.LoadRSAPSSVerifier( + timestampPublicKey.(*rsa.PublicKey), + timestampHash, + &rsa.PSSOptions{Hash: timestampHash}, + ) assert.NoError(t, err) err = timestampVerifier.VerifySignature(bytes.NewReader(timestampSig), bytes.NewReader(data)) assert.NoError(t, err) @@ -587,7 +602,7 @@ func TestMetadataVerifyDelegate(t *testing.T) { // Verify succeeds when we correct the new signature and reach the // threshold of 2 keys - signer, err := signature.LoadSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "timestamp_key"), crypto.SHA256, cryptoutils.SkipPassword) + signer, err := rsapss.LoadRSAPSSSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "timestamp_key")) assert.NoError(t, err) _, err = snapshot.Sign(signer) assert.NoError(t, err) diff --git a/metadata/trustedmetadata/trustedmetadata_test.go b/metadata/trustedmetadata/trustedmetadata_test.go index 676bc3b9..86afdddd 100644 --- a/metadata/trustedmetadata/trustedmetadata_test.go +++ b/metadata/trustedmetadata/trustedmetadata_test.go @@ -18,16 +18,15 @@ package trustedmetadata import ( - "crypto" "os" "path/filepath" "testing" "time" - "github.com/sigstore/sigstore/pkg/cryptoutils" - "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/theupdateframework/go-tuf/v2/internal/testutils" + "github.com/theupdateframework/go-tuf/v2/internal/testutils/rsapss" "github.com/theupdateframework/go-tuf/v2/metadata" ) @@ -114,7 +113,7 @@ func modifyRootMetadata(fn modifyRoot) ([]byte, error) { } fn(root) - signer, err := signature.LoadSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "root_key"), crypto.SHA256, cryptoutils.SkipPassword) + signer, err := rsapss.LoadRSAPSSSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "root_key")) if err != nil { log.Error(err, "failed to load signer from pem file") } @@ -137,7 +136,7 @@ func modifyTimestamptMetadata(fn modifyTimestamp) ([]byte, error) { } fn(timestamp) - signer, err := signature.LoadSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "timestamp_key"), crypto.SHA256, cryptoutils.SkipPassword) + signer, err := rsapss.LoadRSAPSSSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "timestamp_key")) if err != nil { log.Error(err, "failed to load signer from pem file") } @@ -160,7 +159,7 @@ func modifySnapshotMetadata(fn modifySnapshot) ([]byte, error) { } fn(snapshot) - signer, err := signature.LoadSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "snapshot_key"), crypto.SHA256, cryptoutils.SkipPassword) + signer, err := rsapss.LoadRSAPSSSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "snapshot_key")) if err != nil { log.Error(err, "failed to load signer from pem file") } @@ -183,7 +182,7 @@ func modifyTargetsMetadata(fn modifyTargets) ([]byte, error) { } fn(targets) - signer, err := signature.LoadSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "targets_key"), crypto.SHA256, cryptoutils.SkipPassword) + signer, err := rsapss.LoadRSAPSSSignerFromPEMFile(filepath.Join(testutils.KeystoreDir, "targets_key")) if err != nil { log.Error(err, "failed to load signer from pem file") } @@ -197,7 +196,7 @@ func modifyTargetsMetadata(fn modifyTargets) ([]byte, error) { func TestUpdate(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) _, err = trustedSet.UpdateSnapshot(allRoles[metadata.SNAPSHOT], false) @@ -219,7 +218,7 @@ func TestUpdate(t *testing.T) { func TestOutOfOrderOps(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) // Update snapshot before timestamp _, err = trustedSet.UpdateSnapshot(allRoles[metadata.SNAPSHOT], false) @@ -260,7 +259,7 @@ func TestOutOfOrderOps(t *testing.T) { func TestRootWithInvalidJson(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) // Test loading initial root and root update @@ -284,7 +283,7 @@ func TestRootWithInvalidJson(t *testing.T) { func TestTopLevelMetadataWithInvalidJSON(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) //TIMESTAMP // timestamp is not json @@ -359,7 +358,7 @@ func TestUpdateRootNewRoot(t *testing.T) { assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateRoot(root) assert.NoError(t, err) } @@ -374,14 +373,14 @@ func TestUpdateRootNewRootFailTreshholdVerification(t *testing.T) { assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateRoot(root) assert.ErrorIs(t, err, &metadata.ErrUnsignedMetadata{Msg: "Verifying root failed, not enough signatures, got 1, want 2"}) } func TestUpdateRootNewRootVerSameAsTrustedRootVer(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateRoot(allRoles[metadata.ROOT]) assert.ErrorIs(t, err, &metadata.ErrBadVersionNumber{Msg: "bad version number, expected 2, got 1"}) @@ -397,7 +396,7 @@ func TestRootExpiredFinalRoot(t *testing.T) { root, err := modifyRootMetadata(modifyRootExpiry) assert.NoError(t, err) trustedSet, err := New(root) - assert.NoError(t, err) + require.NoError(t, err) // Update timestamp to trigger final root expiry check _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) @@ -413,7 +412,7 @@ func TestUpdateTimestampNewTimestampVerBelowTrustedVer(t *testing.T) { assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(timestamp) assert.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) @@ -424,7 +423,7 @@ func TestUpdateTimestampWithSameTimestamp(t *testing.T) { // Test that timestamp is NOT updated if: // newTimestamp.Version = trustedTimestamp.Version trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) @@ -448,7 +447,7 @@ func TestUpdateTimestampSnapshotCerBellowCurrent(t *testing.T) { timestamp, err := modifyTimestamptMetadata(bumpSnapshotVersion) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(timestamp) assert.NoError(t, err) @@ -466,7 +465,7 @@ func TestUpdateTimestampExpired(t *testing.T) { timestamp, err := modifyTimestamptMetadata(modifyTimestampExpiry) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(timestamp) assert.ErrorIs(t, err, &metadata.ErrExpiredMetadata{Msg: "timestamp.json is expired"}) _, err = trustedSet.UpdateSnapshot(allRoles[metadata.SNAPSHOT], false) @@ -481,7 +480,7 @@ func TestUpdateSnapshotLengthOrHashMismatch(t *testing.T) { timestamp, err := modifyTimestamptMetadata(modifySnapshotLength) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(timestamp) assert.NoError(t, err) _, err = trustedSet.UpdateSnapshot(allRoles[metadata.SNAPSHOT], false) @@ -490,7 +489,7 @@ func TestUpdateSnapshotLengthOrHashMismatch(t *testing.T) { func TestUpdateSnapshotFailThreshholdVerification(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) @@ -510,7 +509,7 @@ func TestUpdateSnapshotVersionDivergeTimestampSnapshotVersion(t *testing.T) { timestamp, err := modifyTimestamptMetadata(modifyTimestampVersion) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(timestamp) assert.NoError(t, err) @@ -544,7 +543,7 @@ func updateAllBesidesTargets(trustedSet *TrustedMetadata, timestampBytes []byte, func TestUpdateSnapshotFileRemovedFromMeta(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) err = updateAllBesidesTargets(trustedSet, allRoles[metadata.TIMESTAMP], []byte{}) assert.NoError(t, err) removeFileFromMeta := func(snaphot *metadata.Metadata[metadata.SnapshotType]) { @@ -559,7 +558,7 @@ func TestUpdateSnapshotFileRemovedFromMeta(t *testing.T) { func TestUpdateSnapshotMetaVersionDecreases(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) @@ -577,7 +576,7 @@ func TestUpdateSnapshotMetaVersionDecreases(t *testing.T) { func TestUpdateSnapshotExpiredNewSnapshot(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) @@ -600,7 +599,7 @@ func TestUpdateSnapshotExpiredNewSnapshot(t *testing.T) { func TestUpdateSnapshotSuccessfulRollbackChecks(t *testing.T) { // Load a "local" timestamp, then update to newer one: trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) _, err = trustedSet.UpdateTimestamp(allRoles[metadata.TIMESTAMP]) assert.NoError(t, err) @@ -641,7 +640,7 @@ func TestUpdateTargetsMoMetaInSnapshot(t *testing.T) { snapshot, err := modifySnapshotMetadata(clearMeta) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) err = updateAllBesidesTargets(trustedSet, allRoles[metadata.TIMESTAMP], snapshot) assert.NoError(t, err) @@ -663,7 +662,7 @@ func TestUpdateTargetsHashDiverfeFromSnapshotMetaHash(t *testing.T) { snapshot, err := modifySnapshotMetadata(modifyMetaLength) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) err = updateAllBesidesTargets(trustedSet, allRoles[metadata.TIMESTAMP], snapshot) assert.NoError(t, err) @@ -681,7 +680,7 @@ func TestUpdateTargetsVersionDivergeSnapshotMetaVersion(t *testing.T) { snapshot, err := modifySnapshotMetadata(modifyMeta) assert.NoError(t, err) trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) err = updateAllBesidesTargets(trustedSet, allRoles[metadata.TIMESTAMP], snapshot) assert.NoError(t, err) @@ -692,7 +691,7 @@ func TestUpdateTargetsVersionDivergeSnapshotMetaVersion(t *testing.T) { func TestUpdateTargetsExpiredMewTarget(t *testing.T) { trustedSet, err := New(allRoles[metadata.ROOT]) - assert.NoError(t, err) + require.NoError(t, err) err = updateAllBesidesTargets(trustedSet, allRoles[metadata.TIMESTAMP], allRoles[metadata.SNAPSHOT]) assert.NoError(t, err)