zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/test/signature/notation_test.go (about)

     1  //go:build sync && scrub && metrics && search
     2  // +build sync,scrub,metrics,search
     3  
     4  package signature_test
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"os"
    10  	"path"
    11  	"testing"
    12  
    13  	notconfig "github.com/notaryproject/notation-go/config"
    14  	. "github.com/smartystreets/goconvey/convey"
    15  
    16  	"zotregistry.io/zot/pkg/api"
    17  	"zotregistry.io/zot/pkg/api/config"
    18  	tcommon "zotregistry.io/zot/pkg/test/common"
    19  	. "zotregistry.io/zot/pkg/test/image-utils"
    20  	signature "zotregistry.io/zot/pkg/test/signature"
    21  )
    22  
    23  func TestLoadNotationSigningkeys(t *testing.T) {
    24  	Convey("notation directory doesn't exist", t, func() {
    25  		_, err := signature.LoadNotationSigningkeys(t.TempDir())
    26  		So(err, ShouldNotBeNil)
    27  	})
    28  
    29  	Convey("wrong content of signingkeys.json", t, func() {
    30  		tempDir := t.TempDir()
    31  		dir := path.Join(tempDir, "notation")
    32  		err := os.Mkdir(dir, 0o777)
    33  		So(err, ShouldBeNil)
    34  
    35  		filePath := path.Join(dir, "signingkeys.json")
    36  		err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
    37  		So(err, ShouldBeNil)
    38  
    39  		_, err = signature.LoadNotationSigningkeys(tempDir)
    40  		So(err, ShouldNotBeNil)
    41  	})
    42  
    43  	Convey("not enough permissions to access signingkeys.json", t, func() {
    44  		tempDir := t.TempDir()
    45  		dir := path.Join(tempDir, "notation")
    46  		err := os.Mkdir(dir, 0o777)
    47  		So(err, ShouldBeNil)
    48  
    49  		filePath := path.Join(dir, "signingkeys.json")
    50  		err = os.WriteFile(filePath, []byte("some dummy file content"), 0o300) //nolint: gosec
    51  		So(err, ShouldBeNil)
    52  
    53  		_, err = signature.LoadNotationSigningkeys(tempDir)
    54  		So(err, ShouldNotBeNil)
    55  	})
    56  
    57  	Convey("signingkeys.json not exists so it is created successfully", t, func() {
    58  		tempDir := t.TempDir()
    59  		dir := path.Join(tempDir, "notation")
    60  		err := os.Mkdir(dir, 0o777)
    61  		So(err, ShouldBeNil)
    62  
    63  		_, err = signature.LoadNotationSigningkeys(tempDir)
    64  		So(err, ShouldBeNil)
    65  	})
    66  
    67  	Convey("signingkeys.json not exists - error trying to create it", t, func() {
    68  		tempDir := t.TempDir()
    69  		dir := path.Join(tempDir, "notation")
    70  		// create notation directory without write permissions
    71  		err := os.Mkdir(dir, 0o555)
    72  		So(err, ShouldBeNil)
    73  
    74  		_, err = signature.LoadNotationSigningkeys(tempDir)
    75  		So(err, ShouldNotBeNil)
    76  	})
    77  }
    78  
    79  func TestLoadNotationConfig(t *testing.T) {
    80  	Convey("directory doesn't exist", t, func() {
    81  		_, err := signature.LoadNotationConfig(t.TempDir())
    82  		So(err, ShouldNotBeNil)
    83  	})
    84  
    85  	Convey("wrong content of signingkeys.json", t, func() {
    86  		tempDir := t.TempDir()
    87  		dir := path.Join(tempDir, "notation")
    88  		err := os.Mkdir(dir, 0o777)
    89  		So(err, ShouldBeNil)
    90  
    91  		filePath := path.Join(dir, "signingkeys.json")
    92  		err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
    93  		So(err, ShouldBeNil)
    94  
    95  		_, err = signature.LoadNotationConfig(tempDir)
    96  		So(err, ShouldNotBeNil)
    97  	})
    98  
    99  	Convey("check default value of signature format", t, func() {
   100  		tempDir := t.TempDir()
   101  		dir := path.Join(tempDir, "notation")
   102  		err := os.Mkdir(dir, 0o777)
   103  		So(err, ShouldBeNil)
   104  
   105  		filePath := path.Join(dir, "signingkeys.json")
   106  		err = os.WriteFile(filePath, []byte("{\"SignatureFormat\": \"\"}"), 0o666) //nolint: gosec
   107  		So(err, ShouldBeNil)
   108  
   109  		configInfo, err := signature.LoadNotationConfig(tempDir)
   110  		So(err, ShouldBeNil)
   111  		So(configInfo.SignatureFormat, ShouldEqual, "jws")
   112  	})
   113  }
   114  
   115  func TestSignWithNotation(t *testing.T) {
   116  	Convey("notation directory doesn't exist", t, func() {
   117  		err := signature.SignWithNotation("key", "reference", t.TempDir(), true)
   118  		So(err, ShouldNotBeNil)
   119  	})
   120  
   121  	Convey("key not found", t, func() {
   122  		tempDir := t.TempDir()
   123  		dir := path.Join(tempDir, "notation")
   124  		err := os.Mkdir(dir, 0o777)
   125  		So(err, ShouldBeNil)
   126  
   127  		filePath := path.Join(dir, "signingkeys.json")
   128  		err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
   129  		So(err, ShouldBeNil)
   130  
   131  		err = signature.SignWithNotation("key", "reference", tempDir, true)
   132  		So(err, ShouldEqual, signature.ErrKeyNotFound)
   133  	})
   134  
   135  	Convey("not enough permissions to access notation/localkeys dir", t, func() {
   136  		cwd, err := os.Getwd()
   137  		So(err, ShouldBeNil)
   138  		defer func() { _ = os.Chdir(cwd) }()
   139  		tdir := t.TempDir()
   140  		_ = os.Chdir(tdir)
   141  
   142  		signature.NotationPathLock.Lock()
   143  		defer signature.NotationPathLock.Unlock()
   144  
   145  		signature.LoadNotationPath(tdir)
   146  
   147  		err = signature.GenerateNotationCerts(tdir, "key")
   148  		So(err, ShouldBeNil)
   149  
   150  		err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o000)
   151  		So(err, ShouldBeNil)
   152  
   153  		err = signature.SignWithNotation("key", "reference", tdir, true)
   154  		So(err, ShouldNotBeNil)
   155  
   156  		err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o755)
   157  		So(err, ShouldBeNil)
   158  	})
   159  
   160  	Convey("error parsing reference", t, func() {
   161  		cwd, err := os.Getwd()
   162  		So(err, ShouldBeNil)
   163  		defer func() { _ = os.Chdir(cwd) }()
   164  		tdir := t.TempDir()
   165  		_ = os.Chdir(tdir)
   166  
   167  		signature.NotationPathLock.Lock()
   168  		defer signature.NotationPathLock.Unlock()
   169  
   170  		signature.LoadNotationPath(tdir)
   171  
   172  		err = signature.GenerateNotationCerts(tdir, "key")
   173  		So(err, ShouldBeNil)
   174  
   175  		err = signature.SignWithNotation("key", "invalidReference", tdir, true)
   176  		So(err, ShouldNotBeNil)
   177  	})
   178  
   179  	Convey("error signing", t, func() {
   180  		cwd, err := os.Getwd()
   181  		So(err, ShouldBeNil)
   182  		defer func() { _ = os.Chdir(cwd) }()
   183  		tdir := t.TempDir()
   184  		_ = os.Chdir(tdir)
   185  
   186  		signature.NotationPathLock.Lock()
   187  		defer signature.NotationPathLock.Unlock()
   188  
   189  		signature.LoadNotationPath(tdir)
   190  
   191  		err = signature.GenerateNotationCerts(tdir, "key")
   192  		So(err, ShouldBeNil)
   193  
   194  		err = signature.SignWithNotation("key", "localhost:8080/invalidreference:1.0", tdir, true)
   195  		So(err, ShouldNotBeNil)
   196  	})
   197  }
   198  
   199  func TestVerifyWithNotation(t *testing.T) {
   200  	Convey("notation directory doesn't exist", t, func() {
   201  		err := signature.VerifyWithNotation("reference", t.TempDir())
   202  		So(err, ShouldNotBeNil)
   203  	})
   204  
   205  	Convey("error parsing reference", t, func() {
   206  		cwd, err := os.Getwd()
   207  		So(err, ShouldBeNil)
   208  		defer func() { _ = os.Chdir(cwd) }()
   209  		tdir := t.TempDir()
   210  		_ = os.Chdir(tdir)
   211  
   212  		signature.NotationPathLock.Lock()
   213  		defer signature.NotationPathLock.Unlock()
   214  
   215  		signature.LoadNotationPath(tdir)
   216  
   217  		err = signature.GenerateNotationCerts(tdir, "key")
   218  		So(err, ShouldBeNil)
   219  
   220  		err = signature.VerifyWithNotation("invalidReference", tdir)
   221  		So(err, ShouldNotBeNil)
   222  	})
   223  
   224  	Convey("error trying to get manifest", t, func() {
   225  		cwd, err := os.Getwd()
   226  		So(err, ShouldBeNil)
   227  		defer func() { _ = os.Chdir(cwd) }()
   228  		tdir := t.TempDir()
   229  		_ = os.Chdir(tdir)
   230  
   231  		signature.NotationPathLock.Lock()
   232  		defer signature.NotationPathLock.Unlock()
   233  
   234  		signature.LoadNotationPath(tdir)
   235  
   236  		err = signature.GenerateNotationCerts(tdir, "key")
   237  		So(err, ShouldBeNil)
   238  
   239  		err = signature.VerifyWithNotation("localhost:8080/invalidreference:1.0", tdir)
   240  		So(err, ShouldNotBeNil)
   241  	})
   242  
   243  	Convey("invalid content of trustpolicy.json", t, func() {
   244  		// start a new server
   245  		port := tcommon.GetFreePort()
   246  		baseURL := tcommon.GetBaseURL(port)
   247  		dir := t.TempDir()
   248  
   249  		conf := config.New()
   250  		conf.HTTP.Port = port
   251  		conf.Storage.RootDirectory = dir
   252  
   253  		ctlr := api.NewController(conf)
   254  		cm := tcommon.NewControllerManager(ctlr)
   255  		// this blocks
   256  		cm.StartAndWait(port)
   257  		defer cm.StopServer()
   258  
   259  		repoName := "signed-repo"
   260  		tag := "1.0"
   261  
   262  		image := CreateRandomImage()
   263  
   264  		err := UploadImage(image, baseURL, repoName, tag)
   265  		So(err, ShouldBeNil)
   266  
   267  		tempDir := t.TempDir()
   268  		notationDir := path.Join(tempDir, "notation")
   269  		err = os.Mkdir(notationDir, 0o777)
   270  		So(err, ShouldBeNil)
   271  
   272  		filePath := path.Join(notationDir, "trustpolicy.json")
   273  		err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
   274  		So(err, ShouldBeNil)
   275  
   276  		signature.NotationPathLock.Lock()
   277  		defer signature.NotationPathLock.Unlock()
   278  
   279  		signature.LoadNotationPath(tempDir)
   280  
   281  		err = signature.VerifyWithNotation(fmt.Sprintf("localhost:%s/%s:%s", port, repoName, tag), tempDir)
   282  		So(err, ShouldNotBeNil)
   283  	})
   284  }
   285  
   286  func TestListNotarySignatures(t *testing.T) {
   287  	Convey("error parsing reference", t, func() {
   288  		cwd, err := os.Getwd()
   289  		So(err, ShouldBeNil)
   290  		defer func() { _ = os.Chdir(cwd) }()
   291  		tdir := t.TempDir()
   292  		_ = os.Chdir(tdir)
   293  
   294  		_, err = signature.ListNotarySignatures("invalidReference", tdir)
   295  		So(err, ShouldNotBeNil)
   296  	})
   297  
   298  	Convey("error trying to get manifest", t, func() {
   299  		cwd, err := os.Getwd()
   300  		So(err, ShouldBeNil)
   301  		defer func() { _ = os.Chdir(cwd) }()
   302  		tdir := t.TempDir()
   303  		_ = os.Chdir(tdir)
   304  
   305  		_, err = signature.ListNotarySignatures("localhost:8080/invalidreference:1.0", tdir)
   306  		So(err, ShouldNotBeNil)
   307  	})
   308  }
   309  
   310  func TestGenerateNotationCerts(t *testing.T) {
   311  	Convey("write key file with permission", t, func() {
   312  		tempDir := t.TempDir()
   313  
   314  		notationDir := path.Join(tempDir, "notation")
   315  		err := os.Mkdir(notationDir, 0o777)
   316  		So(err, ShouldBeNil)
   317  
   318  		filePath := path.Join(notationDir, "localkeys")
   319  		err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
   320  		So(err, ShouldBeNil)
   321  
   322  		signature.NotationPathLock.Lock()
   323  		defer signature.NotationPathLock.Unlock()
   324  
   325  		signature.LoadNotationPath(tempDir)
   326  
   327  		err = signature.GenerateNotationCerts(t.TempDir(), "cert")
   328  		So(err, ShouldNotBeNil)
   329  	})
   330  
   331  	Convey("write cert file with permission", t, func() {
   332  		tempDir := t.TempDir()
   333  
   334  		notationDir := path.Join(tempDir, "notation", "localkeys")
   335  		err := os.MkdirAll(notationDir, 0o777)
   336  		So(err, ShouldBeNil)
   337  
   338  		filePath := path.Join(notationDir, "cert.crt")
   339  		err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
   340  		So(err, ShouldBeNil)
   341  
   342  		err = os.Chmod(filePath, 0o000)
   343  		So(err, ShouldBeNil)
   344  
   345  		signature.NotationPathLock.Lock()
   346  		defer signature.NotationPathLock.Unlock()
   347  
   348  		signature.LoadNotationPath(tempDir)
   349  
   350  		err = signature.GenerateNotationCerts(t.TempDir(), "cert")
   351  		So(err, ShouldNotBeNil)
   352  
   353  		err = os.Chmod(filePath, 0o755)
   354  		So(err, ShouldBeNil)
   355  	})
   356  
   357  	Convey("signingkeys.json file - not enough permission", t, func() {
   358  		tempDir := t.TempDir()
   359  
   360  		notationDir := path.Join(tempDir, "notation")
   361  		err := os.Mkdir(notationDir, 0o777)
   362  		So(err, ShouldBeNil)
   363  
   364  		filePath := path.Join(notationDir, "signingkeys.json")
   365  		_, err = os.Create(filePath) //nolint: gosec
   366  		So(err, ShouldBeNil)
   367  		err = os.Chmod(filePath, 0o000)
   368  		So(err, ShouldBeNil)
   369  
   370  		signature.NotationPathLock.Lock()
   371  		defer signature.NotationPathLock.Unlock()
   372  
   373  		signature.LoadNotationPath(tempDir)
   374  
   375  		err = signature.GenerateNotationCerts(t.TempDir(), "cert")
   376  		So(err, ShouldNotBeNil)
   377  
   378  		err = os.Remove(filePath)
   379  		So(err, ShouldBeNil)
   380  		err = os.RemoveAll(path.Join(notationDir, "localkeys"))
   381  		So(err, ShouldBeNil)
   382  		signingKeysBuf, err := json.Marshal(notconfig.SigningKeys{})
   383  		So(err, ShouldBeNil)
   384  		err = os.WriteFile(filePath, signingKeysBuf, 0o555) //nolint:gosec // test code
   385  		So(err, ShouldBeNil)
   386  		err = signature.GenerateNotationCerts(t.TempDir(), "cert")
   387  		So(err, ShouldNotBeNil)
   388  	})
   389  	Convey("keysuite already exists in signingkeys.json", t, func() {
   390  		tempDir := t.TempDir()
   391  
   392  		notationDir := path.Join(tempDir, "notation")
   393  		err := os.Mkdir(notationDir, 0o777)
   394  		So(err, ShouldBeNil)
   395  
   396  		certName := "cert-test"
   397  		filePath := path.Join(notationDir, "signingkeys.json")
   398  		keyPath := path.Join(notationDir, "localkeys", certName+".key")
   399  		certPath := path.Join(notationDir, "localkeys", certName+".crt")
   400  		signingKeys := notconfig.SigningKeys{}
   401  		keySuite := notconfig.KeySuite{
   402  			Name: certName,
   403  			X509KeyPair: &notconfig.X509KeyPair{
   404  				KeyPath:         keyPath,
   405  				CertificatePath: certPath,
   406  			},
   407  		}
   408  		signingKeys.Keys = []notconfig.KeySuite{keySuite}
   409  		signingKeysBuf, err := json.Marshal(signingKeys)
   410  		So(err, ShouldBeNil)
   411  		err = os.WriteFile(filePath, signingKeysBuf, 0o600)
   412  		So(err, ShouldBeNil)
   413  
   414  		signature.NotationPathLock.Lock()
   415  		defer signature.NotationPathLock.Unlock()
   416  
   417  		signature.LoadNotationPath(tempDir)
   418  
   419  		err = signature.GenerateNotationCerts(t.TempDir(), certName)
   420  		So(err, ShouldNotBeNil)
   421  	})
   422  	Convey("truststore files", t, func() {
   423  		tempDir := t.TempDir()
   424  
   425  		notationDir := path.Join(tempDir, "notation")
   426  		err := os.Mkdir(notationDir, 0o777)
   427  		So(err, ShouldBeNil)
   428  
   429  		certName := "cert-test"
   430  		trustStorePath := path.Join(notationDir, fmt.Sprintf("truststore/x509/ca/%s", certName))
   431  		err = os.MkdirAll(trustStorePath, 0o755)
   432  		So(err, ShouldBeNil)
   433  		err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o000)
   434  		So(err, ShouldBeNil)
   435  
   436  		signature.NotationPathLock.Lock()
   437  		defer signature.NotationPathLock.Unlock()
   438  
   439  		signature.LoadNotationPath(tempDir)
   440  
   441  		err = signature.GenerateNotationCerts(tempDir, certName)
   442  		So(err, ShouldNotBeNil)
   443  
   444  		err = os.RemoveAll(path.Join(notationDir, "localkeys"))
   445  		So(err, ShouldBeNil)
   446  		err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o755)
   447  		So(err, ShouldBeNil)
   448  		_, err = os.Create(path.Join(trustStorePath, "cert-test.crt"))
   449  		So(err, ShouldBeNil)
   450  
   451  		err = signature.GenerateNotationCerts(tempDir, certName)
   452  		So(err, ShouldNotBeNil)
   453  
   454  		err = os.RemoveAll(path.Join(notationDir, "localkeys"))
   455  		So(err, ShouldBeNil)
   456  		err = os.Remove(path.Join(trustStorePath, "cert-test.crt"))
   457  		So(err, ShouldBeNil)
   458  		err = os.Chmod(path.Join(notationDir, "truststore/x509/ca", certName), 0o555)
   459  		So(err, ShouldBeNil)
   460  
   461  		err = signature.GenerateNotationCerts(tempDir, certName)
   462  		So(err, ShouldNotBeNil)
   463  	})
   464  }