Compare commits

...

3 Commits
main ... v1.2.0

Author SHA1 Message Date
Patrick Zheng a6ca9ee64a
bump: release v1.2.0
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-08-28 09:01:59 +08:00
Patrick Zheng 7260694151
bump: bump up for v1.2.0 release (#442)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 08:54:07 +08:00
Patrick Zheng a83c5be7ef
refactor!: remove blob sign/verify related code (#437)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-08-16 10:13:28 +08:00
20 changed files with 305 additions and 1674 deletions

View File

@ -57,14 +57,8 @@ const (
PathConfigFile = "config.json"
// PathSigningKeys is the signingkeys file relative path.
PathSigningKeys = "signingkeys.json"
// PathTrustPolicy is the OCI trust policy file relative path.
// Deprecated: PathTrustPolicy exists for historical compatibility and should not be used.
// To get OCI trust policy path, use PathOCITrustPolicy.
// PathTrustPolicy is the trust policy file relative path.
PathTrustPolicy = "trustpolicy.json"
// PathOCITrustPolicy is the OCI trust policy file relative path.
PathOCITrustPolicy = "trustpolicy.oci.json"
// PathBlobTrustPolicy is the Blob trust policy file relative path.
PathBlobTrustPolicy = "trustpolicy.blob.json"
// PathPlugins is the plugins directory relative path.
PathPlugins = "plugins"
// LocalKeysDir is the directory name for local key relative path.

View File

@ -1,85 +0,0 @@
// Copyright The Notary Project 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.
package notation_test
import (
"context"
"fmt"
"strings"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/signer"
)
// ExampleSignBlob demonstrates how to use signer.BlobSign to sign arbitrary data.
func Example_signBlob() {
//exampleSigner implements notation.Signer and notation.BlobSigner. Given key and X509 certificate chain,
// it provides method to sign OCI artifacts or blobs.
// Users should replace `exampleCertTuple.PrivateKey` with their own private
// key and replace `exampleCerts` with the corresponding certificate chain,
//following the Notary certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts)
if err != nil {
panic(err) // Handle error
}
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := jws.MediaTypeEnvelope
exampleContentMediaType := "video/mp4"
// exampleSignOptions is an example of notation.SignBlobOptions.
exampleSignOptions := notation.SignBlobOptions{
SignerSignOptions: notation.SignerSignOptions{
SignatureMediaType: exampleSignatureMediaType,
SigningAgent: "example signing agent",
},
ContentMediaType: exampleContentMediaType,
UserMetadata: map[string]string{"buildId": "101"},
}
// exampleReader reads the data that needs to be signed. This data can be in a file or in memory.
exampleReader := strings.NewReader("example blob")
// Upon successful signing, signature envelope and signerInfo are returned.
// signatureEnvelope can be used in a verification process later on.
signatureEnvelope, signerInfo, err := notation.SignBlob(context.Background(), exampleSigner, exampleReader, exampleSignOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully signed")
// a peek of the signature envelope generated
sigBlob, err := signature.ParseEnvelope(exampleSignatureMediaType, signatureEnvelope)
if err != nil {
panic(err) // Handle error
}
sigContent, err := sigBlob.Content()
if err != nil {
panic(err) // Handle error
}
fmt.Println("signature Payload ContentType:", sigContent.Payload.ContentType)
fmt.Println("signature Payload Content:", string(sigContent.Payload.Content))
fmt.Println("signerInfo SigningAgent:", signerInfo.UnsignedAttributes.SigningAgent)
// Output:
// Successfully signed
// signature Payload ContentType: application/vnd.cncf.notary.payload.v1+json
// signature Payload Content: {"targetArtifact":{"annotations":{"buildId":"101"},"digest":"sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a61","mediaType":"video/mp4","size":12}}
// signerInfo SigningAgent: example signing agent
}

View File

@ -1,151 +0,0 @@
// Copyright The Notary Project 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.
package notation_test
import (
"context"
"fmt"
"os"
"strings"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/verifier"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
)
// examplePolicyDocument is an example of a valid trust policy document.
// trust policy document should follow this spec:
// https://github.com/notaryproject/notaryproject/blob/v1.1.0/specs/trust-store-trust-policy.md#trust-policy
var exampleBlobPolicyDocument = trustpolicy.BlobDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.BlobTrustPolicy{
{
Name: "test-statement-name",
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name, Override: map[trustpolicy.ValidationType]trustpolicy.ValidationAction{trustpolicy.TypeRevocation: trustpolicy.ActionSkip}},
TrustStores: []string{"ca:valid-trust-store"},
TrustedIdentities: []string{"*"},
},
},
}
// ExampleVerifyBlob demonstrates how to use verifier.Verify to verify a
// signature of the blob.
func Example_verifyBlob() {
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := jws.MediaTypeEnvelope
// exampleSignatureEnvelope is a valid signature envelope.
exampleSignatureEnvelope := getSignatureEnvelope()
// createTrustStoreForBlobVerify creates a trust store directory for demo purpose.
// Users could use the default trust store from Notary and add trusted
// certificates into it following the trust store spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-store
if err := createTrustStoreForBlobVerify(); err != nil {
panic(err) // Handle error
}
// exampleVerifier implements notation.Verify and notation.VerifyBlob.
exampleVerifier, err := verifier.NewVerifier(nil, &exampleBlobPolicyDocument, truststore.NewX509TrustStore(dir.ConfigFS()), nil)
if err != nil {
panic(err) // Handle error
}
// exampleReader reads the data that needs to be verified. This data can be in a file or in memory.
exampleReader := strings.NewReader("example blob")
// exampleVerifyOptions is an example of notation.VerifierVerifyOptions
exampleVerifyOptions := notation.VerifyBlobOptions{
BlobVerifierVerifyOptions: notation.BlobVerifierVerifyOptions{
SignatureMediaType: exampleSignatureMediaType,
TrustPolicyName: "test-statement-name",
},
}
// upon successful verification, the signature verification outcome is
// returned.
_, outcome, err := notation.VerifyBlob(context.Background(), exampleVerifier, exampleReader, []byte(exampleSignatureEnvelope), exampleVerifyOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully verified")
// a peek of the payload inside the signature envelope
fmt.Println("payload ContentType:", outcome.EnvelopeContent.Payload.ContentType)
// Note, upon successful verification, payload.TargetArtifact from the
// signature envelope matches exactly with our exampleTargetDescriptor.
// (This check has been done for the user inside verifier.Verify.)
fmt.Println("payload Content:", string(outcome.EnvelopeContent.Payload.Content))
// Output:
// Successfully verified
// payload ContentType: application/vnd.cncf.notary.payload.v1+json
// payload Content: {"targetArtifact":{"digest":"sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a61","mediaType":"video/mp4","size":12}}
}
func createTrustStoreForBlobVerify() error {
// changing the path of the trust store for demo purpose.
// Users could keep the default value, i.e. os.UserConfigDir.
dir.UserConfigDir = "tmp"
// an example of a valid X509 self-signed certificate for demo purpose ONLY.
// (This self-signed cert is paired with the private key used to
// generate the `exampleSignatureEnvelopePem` above.)
// Users should replace `exampleX509Certificate` with their own trusted
// certificate and add to the trust store, following the
// Notary certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleX509Certificate := `-----BEGIN CERTIFICATE-----
MIIEbDCCAtSgAwIBAgIBUzANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJVUzEL
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEl
MCMGA1UEAxMcTm90YXRpb24gRXhhbXBsZSBzZWxmLXNpZ25lZDAgFw0yNDA0MDQy
MTIwMjBaGA8yMTI0MDQwNDIxMjAyMFowZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
AldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxJTAjBgNVBAMT
HE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwggGiMA0GCSqGSIb3DQEBAQUA
A4IBjwAwggGKAoIBgQDGIiN4yCjSVqFELZwxK/BMb8BokP587L8oPrZ1g8H7LudB
moLNDT7vF9xccbCfU3yNuOd0WaOgnENiCs81VHidyJsj1Oz3u+0Zn3ng7V+uZr6m
AIO74efA9ClMiY4i4HIt8IAZF57AL2mzDnCITgSWxikf030Il85MI42STvA+qYuz
ZEOp3XvKo8bDgQFvbtgK0HYYMfrka7VDmIWVo0rBMGm5btI8HOYQ0r9aqsrCxLAv
1AQeOQm+wbRcp4R5PIUJr+REGn7JCbOyXg/7qqHXKKmvV5yrGaraw8gZ5pqP/RHK
XUJIfvD0Vf2epJmsvC+6vXkSWtz+cA8J4GQx4J4SXL57hoYkC5qv39SOLzlWls3I
6fgeO+SZ0sceMd8NKlom/L5eOJBfB3bTQB83hq/3bRtjT7/qCMsL3VcndKkS+vGF
JPw5uTH+pmBgHrLr6tRoRRjwRFuZ0dO05AbdjCaxgVDtFI3wNbaXn/1VlRGySQIS
UNWxCrUsSzndeqwmjqsCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
MAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBgQBdi0SaJAaeKBB0I+Fjcbmc
4zRvHE4GDSMSDnAK97nrZCZ9iwKuY4x6mv9lwQe2P3VXROoL9JmONNf0yaObOwQj
ILGnbe2rzYtUardz2gzh+6KNzJHspRvk1f06mp4496XQ3STMRSr8kno1svKQMy0Y
FRsGMKs4fWHavIAqNXg9ymrZvvXiatN2UiVtAA/jBFScZAWskeb2WHNzORi7H5Z1
mp5+IlNYQpzdIu/dvLVxzhh2UvkRdsQqsMgt/MOU84RncwUNZM4yI5EGPoaSJdsj
AGNd+UV6ur7QmVI2Q9EZNRlaDJtaoZmKns5j1SlmDXWKbdRmw42ORDudODj/pHA9
+u+ca9t3uLsbqO9yPm8m+6fyxffWS11QAH6O7EjydJWcEe5tYkPpL6kcaEyQKESm
5CDlsk+W3ElpaUu6tsnGKODvgdAN3m0noC+qxzCMqoCM4+M5V6OptR98MDl2FK0B
5+WF6YHBxf/uqDvFktUczjrIWuyfECywp05bpGAErGE=
-----END CERTIFICATE-----`
// Adding the certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil {
return err
}
return os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationBlobExample.pem", []byte(exampleX509Certificate), 0600)
}
func getSignatureEnvelope() string {
return `{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEzODQ6YjhhYjI0ZGFmYmE1Y2Y3ZTRjODljNTYyZjgxMWNmMTA0OTNkNDIwM2RhOTgyZDNiMTM0NWYzNjZjYTg2M2Q5YzJlZDMyM2RiZDBmYjdmZjgzYTgwMzAyY2VmZmE1YTYxIiwibWVkaWFUeXBlIjoidmlkZW8vbXA0Iiwic2l6ZSI6MTJ9fQ","protected":"eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI0LTA0LTA0VDE0OjIwOjIxLTA3OjAwIn0","header":{"x5c":["MIIEbDCCAtSgAwIBAgIBUzANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTElMCMGA1UEAxMcTm90YXRpb24gRXhhbXBsZSBzZWxmLXNpZ25lZDAgFw0yNDA0MDQyMTIwMjBaGA8yMTI0MDQwNDIxMjAyMFowZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxJTAjBgNVBAMTHE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDGIiN4yCjSVqFELZwxK/BMb8BokP587L8oPrZ1g8H7LudBmoLNDT7vF9xccbCfU3yNuOd0WaOgnENiCs81VHidyJsj1Oz3u+0Zn3ng7V+uZr6mAIO74efA9ClMiY4i4HIt8IAZF57AL2mzDnCITgSWxikf030Il85MI42STvA+qYuzZEOp3XvKo8bDgQFvbtgK0HYYMfrka7VDmIWVo0rBMGm5btI8HOYQ0r9aqsrCxLAv1AQeOQm+wbRcp4R5PIUJr+REGn7JCbOyXg/7qqHXKKmvV5yrGaraw8gZ5pqP/RHKXUJIfvD0Vf2epJmsvC+6vXkSWtz+cA8J4GQx4J4SXL57hoYkC5qv39SOLzlWls3I6fgeO+SZ0sceMd8NKlom/L5eOJBfB3bTQB83hq/3bRtjT7/qCMsL3VcndKkS+vGFJPw5uTH+pmBgHrLr6tRoRRjwRFuZ0dO05AbdjCaxgVDtFI3wNbaXn/1VlRGySQISUNWxCrUsSzndeqwmjqsCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBgQBdi0SaJAaeKBB0I+Fjcbmc4zRvHE4GDSMSDnAK97nrZCZ9iwKuY4x6mv9lwQe2P3VXROoL9JmONNf0yaObOwQjILGnbe2rzYtUardz2gzh+6KNzJHspRvk1f06mp4496XQ3STMRSr8kno1svKQMy0YFRsGMKs4fWHavIAqNXg9ymrZvvXiatN2UiVtAA/jBFScZAWskeb2WHNzORi7H5Z1mp5+IlNYQpzdIu/dvLVxzhh2UvkRdsQqsMgt/MOU84RncwUNZM4yI5EGPoaSJdsjAGNd+UV6ur7QmVI2Q9EZNRlaDJtaoZmKns5j1SlmDXWKbdRmw42ORDudODj/pHA9+u+ca9t3uLsbqO9yPm8m+6fyxffWS11QAH6O7EjydJWcEe5tYkPpL6kcaEyQKESm5CDlsk+W3ElpaUu6tsnGKODvgdAN3m0noC+qxzCMqoCM4+M5V6OptR98MDl2FK0B5+WF6YHBxf/uqDvFktUczjrIWuyfECywp05bpGAErGE="],"io.cncf.notary.signingAgent":"example signing agent"},"signature":"liOjdgQ9BKuQTZGXRh3o6P8AMUIq_MKQReEcqA5h8M4RYs3DV_wXfaLCr2x_NRcwjTZsoO1_J77hmzkkk4L0IuFP8Qw0KKtmc83G0yFi4yYV5fwzrIbnhC2GRLuqLPnK-C4qYmv52ld3ebvo7XWwRHu30-VXePmTRFp6iG-eSAgkNgwhxSZ0ZmTFLG3ceNiX2bxpLHlXdPwA3aFKbd6nKrzo4CZ1ZyLNmAIaoA5-kmc0Hyt45trpxaaiWusI_pcTLw71YCqEAs32tEq3q6hRAgAZZN-Qvm9GyNp9EuaPiKjMbJFqtjome5ITxyNd-5t09dDCUgSe3t-iqv2Blm4E080AP1TYwUKLYklGniUP1dAtOau5G2juZLpl7tr4LQ99mycflnAmV7e79eEWXffvy5EAl77dW4_vM7lEemm08m2wddGuDOWXYb1j1r2_a5Xb92umHq6ZMhAp200A0pUkm9640x8z5jdudi_7KeezdqUK7ZMmSxHohiylyKD_20Cy"}`
}

8
go.mod
View File

@ -1,17 +1,17 @@
module github.com/notaryproject/notation-go
go 1.21
go 1.22
require (
github.com/go-ldap/ldap/v3 v3.4.8
github.com/notaryproject/notation-core-go v1.1.0-rc.1
github.com/notaryproject/notation-core-go v1.1.0
github.com/notaryproject/notation-plugin-framework-go v1.0.0
github.com/notaryproject/tspclient-go v0.2.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/veraison/go-cose v1.1.0
golang.org/x/crypto v0.25.0
golang.org/x/mod v0.19.0
golang.org/x/crypto v0.26.0
golang.org/x/mod v0.20.0
oras.land/oras-go/v2 v2.5.0
)

12
go.sum
View File

@ -32,8 +32,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/notaryproject/notation-core-go v1.1.0-rc.1 h1:6cxfVUuc4rTqYu0u7vOmgXfqw1zZabSLJNo8KvkDEzU=
github.com/notaryproject/notation-core-go v1.1.0-rc.1/go.mod h1:j6NELapik2bE1DcrL5otTfXWuW5PR/JLLfREZ4ggmYY=
github.com/notaryproject/notation-core-go v1.1.0 h1:xCybcONOKcCyPNihJUSa+jRNsyQFNkrk0eJVVs1kWeg=
github.com/notaryproject/notation-core-go v1.1.0/go.mod h1:+6AOh41JPrnVLbW/19SJqdhVHwKgIINBO/np0e7nXJA=
github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4=
github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics=
github.com/notaryproject/tspclient-go v0.2.0 h1:g/KpQGmyk/h7j60irIRG1mfWnibNOzJ8WhLqAzuiQAQ=
@ -62,12 +62,12 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=

View File

@ -23,8 +23,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"mime"
"strings"
"time"
@ -80,35 +78,6 @@ type Signer interface {
Sign(ctx context.Context, desc ocispec.Descriptor, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error)
}
// SignBlobOptions contains parameters for notation.SignBlob.
type SignBlobOptions struct {
SignerSignOptions
// ContentMediaType is the media-type of the blob being signed.
ContentMediaType string
// UserMetadata contains key-value pairs that are added to the signature
// payload
UserMetadata map[string]string
}
// BlobDescriptorGenerator creates descriptor using the digest Algorithm.
// Below is the example of minimal descriptor, it must contain mediatype, digest and size of the artifact
//
// {
// "mediaType": "application/octet-stream",
// "digest": "sha256:2f3a23b6373afb134ddcd864be8e037e34a662d090d33ee849471ff73c873345",
// "size": 1024
// }
type BlobDescriptorGenerator func(digest.Algorithm) (ocispec.Descriptor, error)
// BlobSigner is a generic interface for signing arbitrary data.
// The interface allows signing with local or remote keys,
// and packing in various signature formats.
type BlobSigner interface {
// SignBlob signs the descriptor returned by genDesc ,
// and returns the signature and SignerInfo
SignBlob(ctx context.Context, genDesc BlobDescriptorGenerator, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error)
}
// signerAnnotation facilitates return of manifest annotations by signers
type signerAnnotation interface {
// PluginAnnotations returns signature manifest annotations returned from
@ -194,29 +163,6 @@ func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts
return targetDesc, nil
}
// SignBlob signs the arbitrary data and returns the signature
func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, signBlobOpts SignBlobOptions) ([]byte, *signature.SignerInfo, error) {
// sanity checks
if err := validateSignArguments(signer, signBlobOpts.SignerSignOptions); err != nil {
return nil, nil, err
}
if blobReader == nil {
return nil, nil, errors.New("blobReader cannot be nil")
}
if signBlobOpts.ContentMediaType == "" {
return nil, nil, errors.New("content media-type cannot be empty")
}
if err := validateContentMediaType(signBlobOpts.ContentMediaType); err != nil {
return nil, nil, err
}
getDescFunc := getDescriptorFunc(ctx, blobReader, signBlobOpts.ContentMediaType, signBlobOpts.UserMetadata)
return signer.SignBlob(ctx, getDescFunc, signBlobOpts.SignerSignOptions)
}
func validateSignArguments(signer any, signOpts SignerSignOptions) error {
if signer == nil {
return errors.New("signer cannot be nil")
@ -348,33 +294,6 @@ type Verifier interface {
Verify(ctx context.Context, desc ocispec.Descriptor, signature []byte, opts VerifierVerifyOptions) (*VerificationOutcome, error)
}
// BlobVerifierVerifyOptions contains parameters for BlobVerifier.Verify.
type BlobVerifierVerifyOptions struct {
// SignatureMediaType is the envelope type of the signature.
// Currently only `application/jose+json` and `application/cose` are
// supported.
SignatureMediaType string
// PluginConfig is a map of plugin configs.
PluginConfig map[string]string
// UserMetadata contains key-value pairs that must be present in the
// signature.
UserMetadata map[string]string
// TrustPolicyName is the name of trust policy picked by caller.
// If empty, the global trust policy will be applied.
TrustPolicyName string
}
// BlobVerifier is a generic interface for verifying a blob.
type BlobVerifier interface {
// VerifyBlob verifies the `signature` against the target artifact using the
// descriptor returned by descGenFunc parameter and
// returns the outcome upon successful verification.
VerifyBlob(ctx context.Context, descGenFunc BlobDescriptorGenerator, signature []byte, opts BlobVerifierVerifyOptions) (*VerificationOutcome, error)
}
type verifySkipper interface {
// SkipVerify validates whether the verification level is skip.
SkipVerify(ctx context.Context, opts VerifierVerifyOptions) (bool, *trustpolicy.VerificationLevel, error)
@ -399,55 +318,6 @@ type VerifyOptions struct {
UserMetadata map[string]string
}
// VerifyBlobOptions contains parameters for notation.VerifyBlob.
type VerifyBlobOptions struct {
BlobVerifierVerifyOptions
// ContentMediaType is the media-type type of the content being verified.
ContentMediaType string
}
// VerifyBlob performs signature verification for a blob using notation supported
// verification types (like integrity, authenticity, etc.) and return the
// successful signature verification outcome. The blob is read using blobReader and
// upon successful verification, it returns the descriptor of the blob.
// For more details on signature verification, see
// https://github.com/notaryproject/notaryproject/blob/main/specs/trust-store-trust-policy.md#signature-verification
func VerifyBlob(ctx context.Context, blobVerifier BlobVerifier, blobReader io.Reader, signature []byte, verifyBlobOpts VerifyBlobOptions) (ocispec.Descriptor, *VerificationOutcome, error) {
if blobVerifier == nil {
return ocispec.Descriptor{}, nil, errors.New("blobVerifier cannot be nil")
}
if blobReader == nil {
return ocispec.Descriptor{}, nil, errors.New("blobReader cannot be nil")
}
if len(signature) == 0 {
return ocispec.Descriptor{}, nil, errors.New("signature cannot be nil or empty")
}
if err := validateContentMediaType(verifyBlobOpts.ContentMediaType); err != nil {
return ocispec.Descriptor{}, nil, err
}
if err := validateSigMediaType(verifyBlobOpts.SignatureMediaType); err != nil {
return ocispec.Descriptor{}, nil, err
}
getDescFunc := getDescriptorFunc(ctx, blobReader, verifyBlobOpts.ContentMediaType, verifyBlobOpts.UserMetadata)
vo, err := blobVerifier.VerifyBlob(ctx, getDescFunc, signature, verifyBlobOpts.BlobVerifierVerifyOptions)
if err != nil {
return ocispec.Descriptor{}, nil, err
}
var desc ocispec.Descriptor
if err = json.Unmarshal(vo.EnvelopeContent.Payload.Content, &desc); err != nil {
return ocispec.Descriptor{}, nil, err
}
return desc, vo, nil
}
// Verify performs signature verification on each of the notation supported
// verification types (like integrity, authenticity, etc.) and return the
// successful signature verification outcome.
@ -611,31 +481,6 @@ func generateAnnotations(signerInfo *signature.SignerInfo, annotations map[strin
return annotations, nil
}
func getDescriptorFunc(ctx context.Context, reader io.Reader, contentMediaType string, userMetadata map[string]string) BlobDescriptorGenerator {
return func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) {
digester := hashAlgo.Digester()
bytes, err := io.Copy(digester.Hash(), reader)
if err != nil {
return ocispec.Descriptor{}, err
}
targetDesc := ocispec.Descriptor{
MediaType: contentMediaType,
Digest: digester.Digest(),
Size: bytes,
}
return addUserMetadataToDescriptor(ctx, targetDesc, userMetadata)
}
}
func validateContentMediaType(contentMediaType string) error {
if contentMediaType != "" {
if _, _, err := mime.ParseMediaType(contentMediaType); err != nil {
return fmt.Errorf("invalid content media-type %q: %v", contentMediaType, err)
}
}
return nil
}
func validateSigMediaType(sigMediaType string) error {
if !(sigMediaType == jws.MediaTypeEnvelope || sigMediaType == cose.MediaTypeEnvelope) {
return fmt.Errorf("invalid signature media-type %q", sigMediaType)

View File

@ -18,11 +18,9 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"math"
"os"
"path/filepath"
"strings"
"testing"
"time"
@ -37,7 +35,6 @@ import (
"github.com/notaryproject/notation-go/plugin"
"github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -68,85 +65,6 @@ func TestSignSuccess(t *testing.T) {
}
}
func TestSignBlobSuccess(t *testing.T) {
reader := strings.NewReader("some content")
testCases := []struct {
name string
dur time.Duration
mtype string
agent string
pConfig map[string]string
metadata map[string]string
}{
{"expiryInHours", 24 * time.Hour, "video/mp4", "", nil, nil},
{"oneSecondExpiry", 1 * time.Second, "video/mp4", "", nil, nil},
{"zeroExpiry", 0, "video/mp4", "", nil, nil},
{"validContentType", 1 * time.Second, "video/mp4", "", nil, nil},
{"emptyContentType", 1 * time.Second, "video/mp4", "someDummyAgent", map[string]string{"hi": "hello"}, map[string]string{"bye": "tata"}},
}
for _, tc := range testCases {
t.Run(tc.name, func(b *testing.T) {
opts := SignBlobOptions{
SignerSignOptions: SignerSignOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
ExpiryDuration: tc.dur,
PluginConfig: tc.pConfig,
SigningAgent: tc.agent,
},
UserMetadata: expectedMetadata,
ContentMediaType: tc.mtype,
}
_, _, err := SignBlob(context.Background(), &dummySigner{}, reader, opts)
if err != nil {
b.Fatalf("Sign failed with error: %v", err)
}
})
}
}
func TestSignBlobError(t *testing.T) {
reader := strings.NewReader("some content")
testCases := []struct {
name string
signer BlobSigner
dur time.Duration
rdr io.Reader
sigMType string
ctMType string
errMsg string
}{
{"negativeExpiry", &dummySigner{}, -1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "expiry duration cannot be a negative value"},
{"milliSecExpiry", &dummySigner{}, 1 * time.Millisecond, nil, "video/mp4", jws.MediaTypeEnvelope, "expiry duration supports minimum granularity of seconds"},
{"invalidContentMediaType", &dummySigner{}, 1 * time.Second, reader, "video/mp4/zoping", jws.MediaTypeEnvelope, "invalid content media-type \"video/mp4/zoping\": mime: unexpected content after media subtype"},
{"emptyContentMediaType", &dummySigner{}, 1 * time.Second, reader, "", jws.MediaTypeEnvelope, "content media-type cannot be empty"},
{"invalidSignatureMediaType", &dummySigner{}, 1 * time.Second, reader, "", "", "content media-type cannot be empty"},
{"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "blobReader cannot be nil"},
{"nilSigner", nil, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "signer cannot be nil"},
{"signerError", &dummySigner{fail: true}, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "expected SignBlob failure"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := SignBlobOptions{
SignerSignOptions: SignerSignOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
ExpiryDuration: tc.dur,
PluginConfig: nil,
},
ContentMediaType: tc.sigMType,
}
_, _, err := SignBlob(context.Background(), tc.signer, tc.rdr, opts)
if err == nil {
t.Fatalf("expected error but didnt found")
}
if err.Error() != tc.errMsg {
t.Fatalf("expected err message to be '%s' but found '%s'", tc.errMsg, err.Error())
}
})
}
}
func TestSignSuccessWithUserMetadata(t *testing.T) {
repo := mock.NewRepository()
opts := SignOptions{}
@ -309,7 +227,6 @@ func TestRegistryResolveError(t *testing.T) {
policyDocument := dummyPolicyDocument()
verifier := dummyVerifier{&policyDocument, mock.PluginManager{}, false, *trustpolicy.LevelStrict}
errorMessage := "network error"
expectedErr := ErrorSignatureRetrievalFailed{Msg: errorMessage}
@ -328,7 +245,6 @@ func TestVerifyEmptyReference(t *testing.T) {
policyDocument := dummyPolicyDocument()
verifier := dummyVerifier{&policyDocument, mock.PluginManager{}, false, *trustpolicy.LevelStrict}
errorMessage := "reference is missing digest or tag"
expectedErr := ErrorSignatureRetrievalFailed{Msg: errorMessage}
@ -529,63 +445,6 @@ func TestVerifyFailed(t *testing.T) {
})
}
func TestVerifyBlobError(t *testing.T) {
reader := strings.NewReader("some content")
sig := []byte("signature")
testCases := []struct {
name string
verifier BlobVerifier
sig []byte
rdr io.Reader
ctMType string
sigMType string
errMsg string
}{
{"nilVerifier", nil, sig, reader, "video/mp4", jws.MediaTypeEnvelope, "blobVerifier cannot be nil"},
{"verifierError", &dummyVerifier{FailVerify: true}, sig, reader, "video/mp4", jws.MediaTypeEnvelope, "failed verify"},
{"nilSignature", &dummyVerifier{}, nil, reader, "video/mp4", jws.MediaTypeEnvelope, "signature cannot be nil or empty"},
{"emptySignature", &dummyVerifier{}, []byte{}, reader, "video/mp4", jws.MediaTypeEnvelope, "signature cannot be nil or empty"},
{"nilReader", &dummyVerifier{}, sig, nil, "video/mp4", jws.MediaTypeEnvelope, "blobReader cannot be nil"},
{"invalidContentType", &dummyVerifier{}, sig, reader, "video/mp4/zoping", jws.MediaTypeEnvelope, "invalid content media-type \"video/mp4/zoping\": mime: unexpected content after media subtype"},
{"invalidSigType", &dummyVerifier{}, sig, reader, "video/mp4", "hola!", "invalid signature media-type \"hola!\""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := VerifyBlobOptions{
BlobVerifierVerifyOptions: BlobVerifierVerifyOptions{
SignatureMediaType: tc.sigMType,
UserMetadata: nil,
TrustPolicyName: "",
},
ContentMediaType: tc.ctMType,
}
_, _, err := VerifyBlob(context.Background(), tc.verifier, tc.rdr, tc.sig, opts)
if err == nil {
t.Fatalf("expected error but didnt found")
}
if err.Error() != tc.errMsg {
t.Fatalf("expected err message to be '%s' but found '%s'", tc.errMsg, err.Error())
}
})
}
}
func TestVerifyBlobValid(t *testing.T) {
opts := VerifyBlobOptions{
BlobVerifierVerifyOptions: BlobVerifierVerifyOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
UserMetadata: nil,
TrustPolicyName: "",
},
}
_, _, err := VerifyBlob(context.Background(), &dummyVerifier{}, strings.NewReader("some content"), []byte("signature"), opts)
if err != nil {
t.Fatalf("SignaureMediaTypeMismatch expected: %v got: %v", nil, err)
}
}
func dummyPolicyDocument() (policyDoc trustpolicy.Document) {
policyDoc = trustpolicy.Document{
Version: "1.0",
@ -605,10 +464,7 @@ func dummyPolicyStatement() (policyStatement trustpolicy.TrustPolicy) {
return
}
type dummySigner struct {
fail bool
}
type dummySigner struct{}
func (s *dummySigner) Sign(_ context.Context, _ ocispec.Descriptor, _ SignerSignOptions) ([]byte, *signature.SignerInfo, error) {
return []byte("ABC"), &signature.SignerInfo{
@ -618,23 +474,6 @@ func (s *dummySigner) Sign(_ context.Context, _ ocispec.Descriptor, _ SignerSign
}, nil
}
func (s *dummySigner) SignBlob(_ context.Context, descGenFunc BlobDescriptorGenerator, _ SignerSignOptions) ([]byte, *signature.SignerInfo, error) {
if s.fail {
return nil, nil, errors.New("expected SignBlob failure")
}
_, err := descGenFunc(digest.SHA384)
if err != nil {
return nil, nil, err
}
return []byte("ABC"), &signature.SignerInfo{
SignedAttributes: signature.SignedAttributes{
SigningTime: time.Now(),
},
}, nil
}
type verifyMetadataSigner struct{}
func (s *verifyMetadataSigner) Sign(_ context.Context, desc ocispec.Descriptor, _ SignerSignOptions) ([]byte, *signature.SignerInfo, error) {
@ -651,7 +490,7 @@ func (s *verifyMetadataSigner) Sign(_ context.Context, desc ocispec.Descriptor,
}
type dummyVerifier struct {
TrustPolicyDoc *trustpolicy.OCIDocument
TrustPolicyDoc *trustpolicy.Document
PluginManager plugin.Manager
FailVerify bool
VerificationLevel trustpolicy.VerificationLevel
@ -668,22 +507,6 @@ func (v *dummyVerifier) Verify(_ context.Context, _ ocispec.Descriptor, _ []byte
return outcome, nil
}
func (v *dummyVerifier) VerifyBlob(_ context.Context, _ BlobDescriptorGenerator, _ []byte, _ BlobVerifierVerifyOptions) (*VerificationOutcome, error) {
if v.FailVerify {
return nil, errors.New("failed verify")
}
return &VerificationOutcome{
VerificationResults: []*ValidationResult{},
VerificationLevel: &v.VerificationLevel,
EnvelopeContent: &signature.EnvelopeContent{
Payload: signature.Payload{
Content: []byte("{}"),
},
},
}, nil
}
var (
reference = "sha256:19dbd2e48e921426ee8ace4dc892edfb2ecdc1d1a72d5416c83670c30acecef0"
artifactReference = "local/oci-layout@sha256:19dbd2e48e921426ee8ace4dc892edfb2ecdc1d1a72d5416c83670c30acecef0"

View File

@ -36,21 +36,25 @@ import (
var executor commander = &execCommander{} // for unit test
// GenericPlugin is the base requirement to be a plugin.
//
// Deprecated: GenericPlugin exists for historical compatibility and should not be used.
// To access GenericPlugin, use the notation-plugin-framework-go's plugin.GenericPlugin type.
type GenericPlugin = plugin.GenericPlugin
// SignPlugin defines the required methods to be a SignPlugin.
//
// Deprecated: SignPlugin exists for historical compatibility and should not be used.
// To access SignPlugin, use the notation-plugin-framework-go's plugin.SignPlugin type.
type SignPlugin = plugin.SignPlugin
// VerifyPlugin defines the required method to be a VerifyPlugin.
//
// Deprecated: VerifyPlugin exists for historical compatibility and should not be used.
// To access VerifyPlugin, use the notation-plugin-framework-go's plugin.VerifyPlugin type.
type VerifyPlugin = plugin.VerifyPlugin
// Plugin defines required methods to be a Plugin.
//
// Deprecated: Plugin exists for historical compatibility and should not be used.
// To access Plugin, use the notation-plugin-framework-go's plugin.Plugin type.
type Plugin = plugin.Plugin

View File

@ -15,7 +15,6 @@ package signer
import (
"context"
"crypto"
"crypto/x509"
"encoding/json"
"errors"
@ -30,7 +29,6 @@ import (
"github.com/notaryproject/notation-go/log"
"github.com/notaryproject/notation-go/plugin/proto"
"github.com/notaryproject/notation-plugin-framework-go/plugin"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -43,15 +41,10 @@ type PluginSigner struct {
manifestAnnotations map[string]string
}
var algorithms = map[crypto.Hash]digest.Algorithm{
crypto.SHA256: digest.SHA256,
crypto.SHA384: digest.SHA384,
crypto.SHA512: digest.SHA512,
}
// NewFromPlugin creates a notation.Signer that signs artifacts and generates
// signatures by delegating the one or more operations to the named plugin,
// as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces.
//
// Deprecated: NewFromPlugin function exists for historical compatibility and should not be used.
// To create PluginSigner, use NewPluginSigner() function.
func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (notation.Signer, error) {
@ -116,38 +109,6 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n
return nil, nil, fmt.Errorf("plugin does not have signing capabilities")
}
// SignBlob signs the arbitrary data and returns the marshalled envelope.
func (s *PluginSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) {
logger := log.GetLogger(ctx)
mergedConfig := s.mergeConfig(opts.PluginConfig)
logger.Debug("Invoking plugin's get-plugin-metadata command")
metadata, err := s.plugin.GetMetadata(ctx, &plugin.GetMetadataRequest{PluginConfig: mergedConfig})
if err != nil {
return nil, nil, err
}
logger.Debug("Invoking plugin's describe-key command")
ks, err := s.getKeySpec(ctx, mergedConfig)
if err != nil {
return nil, nil, err
}
// get descriptor to sign
desc, err := getDescriptor(ks, descGenFunc)
if err != nil {
return nil, nil, err
}
logger.Debugf("Using plugin %v with capabilities %v to sign blob using descriptor %+v", metadata.Name, metadata.Capabilities, desc)
if metadata.HasCapability(plugin.CapabilitySignatureGenerator) {
return s.generateSignature(ctx, desc, opts, ks, metadata, mergedConfig)
} else if metadata.HasCapability(plugin.CapabilityEnvelopeGenerator) {
return s.generateSignatureEnvelope(ctx, desc, opts)
}
return nil, nil, fmt.Errorf("plugin does not have signing capabilities")
}
func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) (signature.KeySpec, error) {
logger := log.GetLogger(ctx)
logger.Debug("Invoking plugin's describe-key command")

View File

@ -32,7 +32,6 @@ import (
"github.com/notaryproject/notation-go/internal/envelope"
"github.com/notaryproject/notation-go/plugin"
"github.com/notaryproject/notation-go/plugin/proto"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -71,16 +70,6 @@ type mockPlugin struct {
keySpec signature.KeySpec
}
func getDescriptorFunc(throwError bool) func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) {
return func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) {
if throwError {
return ocispec.Descriptor{}, errors.New("")
}
return validSignDescriptor, nil
}
}
func newMockPlugin(key crypto.PrivateKey, certs []*x509.Certificate, keySpec signature.KeySpec) *mockPlugin {
return &mockPlugin{
key: key,
@ -336,22 +325,6 @@ func TestPluginSigner_Sign_Valid(t *testing.T) {
}
}
func TestPluginSigner_SignBlob_Valid(t *testing.T) {
for _, envelopeType := range signature.RegisteredEnvelopeTypes() {
for _, keyCert := range keyCertPairCollections {
t.Run(fmt.Sprintf("external plugin,envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) {
keySpec, _ := proto.DecodeKeySpec(proto.KeySpec(keyCert.keySpecName))
pluginSigner := PluginSigner{
plugin: newMockPlugin(keyCert.key, keyCert.certs, keySpec),
}
validSignOpts.SignatureMediaType = envelopeType
data, signerInfo, err := pluginSigner.SignBlob(context.Background(), getDescriptorFunc(false), validSignOpts)
basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err)
})
}
}
}
func TestPluginSigner_SignEnvelope_RunFailed(t *testing.T) {
for _, envelopeType := range signature.RegisteredEnvelopeTypes() {
t.Run(fmt.Sprintf("envelopeType=%v", envelopeType), func(t *testing.T) {

View File

@ -34,7 +34,7 @@ import (
)
// signingAgent is the unprotected header field used by signature.
const signingAgent = "Notation/1.0.0"
const signingAgent = "notation-go/1.2.0"
// GenericSigner implements notation.Signer and embeds signature.Signer
type GenericSigner struct {
@ -42,6 +42,7 @@ type GenericSigner struct {
}
// New returns a builtinSigner given key and cert chain
//
// Deprecated: New function exists for historical compatibility and should not be used.
// To create GenericSigner, use NewGenericSigner() function.
func New(key crypto.PrivateKey, certChain []*x509.Certificate) (notation.Signer, error) {
@ -167,30 +168,3 @@ func (s *GenericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts
}
return sig, &envContent.SignerInfo, nil
}
// SignBlob signs the descriptor returned by blobGen and returns the marshalled envelope
func (s *GenericSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) {
logger := log.GetLogger(ctx)
logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType)
ks, err := s.signer.KeySpec()
if err != nil {
return nil, nil, err
}
desc, err := getDescriptor(ks, descGenFunc)
if err != nil {
return nil, nil, err
}
return s.Sign(ctx, desc, opts)
}
func getDescriptor(ks signature.KeySpec, descGenFunc notation.BlobDescriptorGenerator) (ocispec.Descriptor, error) {
digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()]
if !ok {
return ocispec.Descriptor{}, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm().Hash())
}
return descGenFunc(digestAlg)
}

View File

@ -259,31 +259,6 @@ func TestSignWithTimestamping(t *testing.T) {
}
}
func TestSignBlobWithCertChain(t *testing.T) {
// sign with key
for _, envelopeType := range signature.RegisteredEnvelopeTypes() {
for _, keyCert := range keyCertPairCollections {
t.Run(fmt.Sprintf("envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) {
s, err := NewGenericSigner(keyCert.key, keyCert.certs)
if err != nil {
t.Fatalf("NewSigner() error = %v", err)
}
sOpts := notation.SignerSignOptions{
SignatureMediaType: envelopeType,
}
sig, _, err := s.SignBlob(context.Background(), getDescriptorFunc(false), sOpts)
if err != nil {
t.Fatalf("Sign() error = %v", err)
}
// basic verification
basicVerification(t, sig, envelopeType, keyCert.certs[len(keyCert.certs)-1], nil)
})
}
}
}
func TestSignWithoutExpiry(t *testing.T) {
// sign with key
for _, envelopeType := range signature.RegisteredEnvelopeTypes() {

View File

@ -60,7 +60,7 @@ func TestLoadX509TrustStore(t *testing.T) {
// load "ca" and "signingAuthority" trust store
caStore := "ca:valid-trust-store"
signingAuthorityStore := "signingAuthority:valid-trust-store"
dummyPolicy := dummyOCIPolicyDocument().TrustPolicies[0]
dummyPolicy := dummyPolicyDocument().TrustPolicies[0]
dummyPolicy.TrustStores = []string{caStore, signingAuthorityStore}
dir.UserConfigDir = "testdata"
x509truststore := truststore.NewX509TrustStore(dir.ConfigFS())
@ -138,10 +138,10 @@ func getArtifactDigestFromReference(artifactReference string) (string, error) {
return artifactReference[i+1:], nil
}
func dummyOCIPolicyDocument() (policyDoc trustpolicy.OCIDocument) {
return trustpolicy.OCIDocument{
func dummyPolicyDocument() (policyDoc trustpolicy.Document) {
return trustpolicy.Document{
Version: "1.0",
TrustPolicies: []trustpolicy.OCITrustPolicy{
TrustPolicies: []trustpolicy.TrustPolicy{
{
Name: "test-statement-name",
RegistryScopes: []string{"registry.acme-rockets.io/software/net-monitor"},
@ -153,15 +153,11 @@ func dummyOCIPolicyDocument() (policyDoc trustpolicy.OCIDocument) {
}
}
func dummyBlobPolicyDocument() (policyDoc trustpolicy.BlobDocument) {
return trustpolicy.BlobDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.BlobTrustPolicy{
func dummyInvalidPolicyDocument() (policyDoc trustpolicy.Document) {
return trustpolicy.Document{
TrustPolicies: []trustpolicy.TrustPolicy{
{
Name: "blob-test-statement-name",
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"},
TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"},
TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notary,L=Seattle,ST=WA,C=US"},
Name: "invalid",
},
},
}

View File

@ -1,151 +0,0 @@
// Copyright The Notary Project 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.
package trustpolicy
import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/notaryproject/notation-go/dir"
set "github.com/notaryproject/notation-go/internal/container"
"github.com/notaryproject/notation-go/internal/slices"
)
// BlobDocument represents a trustpolicy.blob.json document
type BlobDocument struct {
// Version of the policy document
Version string `json:"version"`
// TrustPolicies include each policy statement
TrustPolicies []BlobTrustPolicy `json:"trustPolicies"`
}
// BlobTrustPolicy represents a policy statement in the blob policy document
type BlobTrustPolicy struct {
// Name of the policy statement
Name string `json:"name"`
// SignatureVerification setting for this policy statement
SignatureVerification SignatureVerification `json:"signatureVerification"`
// TrustStores this policy statement uses
TrustStores []string `json:"trustStores"`
// TrustedIdentities this policy statement pins
TrustedIdentities []string `json:"trustedIdentities"`
// GlobalPolicy defines if policy statement is global or not
GlobalPolicy bool `json:"globalPolicy,omitempty"`
}
var supportedBlobPolicyVersions = []string{"1.0"}
// LoadBlobDocument loads a trust policy document from a local file system
func LoadBlobDocument() (*BlobDocument, error) {
var doc BlobDocument
err := getDocument(dir.PathBlobTrustPolicy, &doc)
return &doc, err
}
// Validate validates a policy document according to its version's rule set.
// if any rule is violated, returns an error
func (policyDoc *BlobDocument) Validate() error {
// sanity check
if policyDoc == nil {
return errors.New("blob trust policy document cannot be nil")
}
// Validate Version
if policyDoc.Version == "" {
return errors.New("blob trust policy has empty version, version must be specified")
}
if !slices.Contains(supportedBlobPolicyVersions, policyDoc.Version) {
return fmt.Errorf("blob trust policy document uses unsupported version %q", policyDoc.Version)
}
// Validate the policy according to 1.0 rules
if len(policyDoc.TrustPolicies) == 0 {
return errors.New("blob trust policy document can not have zero trust policy statements")
}
policyNames := set.New[string]()
var foundGlobalPolicy bool
for _, statement := range policyDoc.TrustPolicies {
// Verify unique policy statement names across the policy document
if policyNames.Contains(statement.Name) {
return fmt.Errorf("multiple blob trust policy statements use the same name %q, statement names must be unique", statement.Name)
}
if err := validatePolicyCore(statement.Name, statement.SignatureVerification, statement.TrustStores, statement.TrustedIdentities); err != nil {
return fmt.Errorf("blob trust policy: %w", err)
}
if statement.GlobalPolicy {
if foundGlobalPolicy {
return errors.New("multiple blob trust policy statements have globalPolicy set to true. Only one trust policy statement can be marked as global policy")
}
// verificationLevel is skip
if reflect.DeepEqual(statement.SignatureVerification.VerificationLevel, LevelSkip) {
return errors.New("global blob trust policy statement cannot have verification level set to skip")
}
foundGlobalPolicy = true
}
policyNames.Add(statement.Name)
}
return nil
}
// GetApplicableTrustPolicy returns a pointer to the deep copied TrustPolicy for given policy name
// see https://github.com/notaryproject/notaryproject/blob/v1.1.0/specs/trust-store-trust-policy.md#blob-trust-policy
func (policyDoc *BlobDocument) GetApplicableTrustPolicy(policyName string) (*BlobTrustPolicy, error) {
if strings.TrimSpace(policyName) == "" {
return nil, errors.New("policy name cannot be empty")
}
for _, policyStatement := range policyDoc.TrustPolicies {
// exact match
if policyStatement.Name == policyName {
return (&policyStatement).clone(), nil
}
}
return nil, fmt.Errorf("no applicable blob trust policy with name %q", policyName)
}
// GetGlobalTrustPolicy returns a pointer to the deep copy of the TrustPolicy that is marked as global policy
// see https://github.com/notaryproject/notaryproject/blob/v1.1.0/specs/trust-store-trust-policy.md#blob-trust-policy
func (policyDoc *BlobDocument) GetGlobalTrustPolicy() (*BlobTrustPolicy, error) {
for _, policyStatement := range policyDoc.TrustPolicies {
if policyStatement.GlobalPolicy {
return (&policyStatement).clone(), nil
}
}
return nil, fmt.Errorf("no global blob trust policy")
}
// clone returns a pointer to the deeply copied TrustPolicy
func (t *BlobTrustPolicy) clone() *BlobTrustPolicy {
return &BlobTrustPolicy{
Name: t.Name,
SignatureVerification: t.SignatureVerification,
TrustedIdentities: append([]string(nil), t.TrustedIdentities...),
TrustStores: append([]string(nil), t.TrustStores...),
GlobalPolicy: t.GlobalPolicy,
}
}

View File

@ -1,168 +0,0 @@
// Copyright The Notary Project 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.
package trustpolicy
import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/notaryproject/notation-go/dir"
)
func TestLoadBlobDocument(t *testing.T) {
tempRoot := t.TempDir()
dir.UserConfigDir = tempRoot
path := filepath.Join(tempRoot, "trustpolicy.blob.json")
policyJson, _ := json.Marshal(dummyBlobPolicyDocument())
if err := os.WriteFile(path, policyJson, 0600); err != nil {
t.Fatalf("TestLoadBlobDocument write policy file failed. Error: %v", err)
}
t.Cleanup(func() { os.RemoveAll(tempRoot) })
if _, err := LoadBlobDocument(); err != nil {
t.Fatalf("LoadBlobDocument() should not throw error for an existing policy file. Error: %v", err)
}
}
func TestValidate_BlobDocument(t *testing.T) {
policyDoc := dummyBlobPolicyDocument()
if err := policyDoc.Validate(); err != nil {
t.Fatalf("Validate() returned error: %v", err)
}
}
func TestValidate_BlobDocument_Error(t *testing.T) {
// Sanity check
var nilPolicyDoc *BlobDocument
err := nilPolicyDoc.Validate()
if err == nil || err.Error() != "blob trust policy document cannot be nil" {
t.Fatalf("nil policyDoc should return error")
}
// empty Version
policyDoc := dummyBlobPolicyDocument()
policyDoc.Version = ""
err = policyDoc.Validate()
if err == nil || err.Error() != "blob trust policy has empty version, version must be specified" {
t.Fatalf("empty version should return error")
}
// Invalid Version
policyDoc = dummyBlobPolicyDocument()
policyDoc.Version = "invalid"
err = policyDoc.Validate()
if err == nil || err.Error() != "blob trust policy document uses unsupported version \"invalid\"" {
t.Fatalf("invalid version should return error")
}
// No Policy Statements
policyDoc = dummyBlobPolicyDocument()
policyDoc.TrustPolicies = nil
err = policyDoc.Validate()
if err == nil || err.Error() != "blob trust policy document can not have zero trust policy statements" {
t.Fatalf("zero policy statements should return error")
}
// No Policy Statement Name
policyDoc = dummyBlobPolicyDocument()
policyDoc.TrustPolicies[0].Name = ""
err = policyDoc.Validate()
if err == nil || err.Error() != "blob trust policy: a trust policy statement is missing a name, every statement requires a name" {
t.Fatalf("policy statement with no name should return an error")
}
// multiple global rust policy
policyDoc = dummyBlobPolicyDocument()
policyStatement1 := policyDoc.TrustPolicies[0].clone()
policyStatement1.GlobalPolicy = true
policyStatement2 := policyDoc.TrustPolicies[0].clone()
policyStatement2.Name = "test-statement-name-2"
policyStatement2.GlobalPolicy = true
policyDoc.TrustPolicies = []BlobTrustPolicy{*policyStatement1, *policyStatement2}
err = policyDoc.Validate()
if err == nil || err.Error() != "multiple blob trust policy statements have globalPolicy set to true. Only one trust policy statement can be marked as global policy" {
t.Error(err)
t.Fatalf("multiple global blob policy should return error")
}
// Policy Document with duplicate policy statement names
policyDoc = dummyBlobPolicyDocument()
policyStatement1 = policyDoc.TrustPolicies[0].clone()
policyStatement2 = policyDoc.TrustPolicies[0].clone()
policyDoc.TrustPolicies = []BlobTrustPolicy{*policyStatement1, *policyStatement2}
err = policyDoc.Validate()
if err == nil || err.Error() != "multiple blob trust policy statements use the same name \"test-statement-name\", statement names must be unique" {
t.Fatalf("policy statements with same name should return error")
}
}
func TestGetApplicableTrustPolicy(t *testing.T) {
policyDoc := dummyBlobPolicyDocument()
policyStatement := policyDoc.TrustPolicies[0].clone()
policyStatement1 := policyStatement.clone()
policyStatement1.Name = "test-statement-name-1"
policyStatement1.GlobalPolicy = true
policyStatement2 := policyStatement.clone()
policyStatement2.Name = "test-statement-name-2"
policyDoc.TrustPolicies = []BlobTrustPolicy{*policyStatement, *policyStatement1, *policyStatement2}
validateGetApplicableTrustPolicy(t, policyDoc, "test-statement-name-2", policyStatement2)
validateGetApplicableTrustPolicy(t, policyDoc, "test-statement-name", policyStatement)
}
func TestGetApplicableTrustPolicy_Error(t *testing.T) {
policyDoc := dummyBlobPolicyDocument()
t.Run("empty policy name", func(t *testing.T) {
_, err := policyDoc.GetApplicableTrustPolicy("")
if err == nil || err.Error() != "policy name cannot be empty" {
t.Fatalf("GetApplicableTrustPolicy() returned error: %v", err)
}
})
t.Run("non existent policy name", func(t *testing.T) {
_, err := policyDoc.GetApplicableTrustPolicy("blaah")
if err == nil || err.Error() != "no applicable blob trust policy with name \"blaah\"" {
t.Fatalf("GetApplicableTrustPolicy() returned error: %v", err)
}
})
}
func TestGetGlobalTrustPolicy(t *testing.T) {
policyDoc := dummyBlobPolicyDocument()
policyDoc.TrustPolicies[0].GlobalPolicy = true
policy, err := policyDoc.GetGlobalTrustPolicy()
if err != nil {
t.Fatalf("GetGlobalTrustPolicy() returned error: %v", err)
}
if !reflect.DeepEqual(*policy, policyDoc.TrustPolicies[0]) {
t.Fatalf("GetGlobalTrustPolicy() returned unexpected policy")
}
}
func validateGetApplicableTrustPolicy(t *testing.T, policyDoc BlobDocument, policyName string, expectedPolicy *BlobTrustPolicy) {
policy, err := policyDoc.GetApplicableTrustPolicy(policyName)
if err != nil {
t.Fatalf("GetApplicableTrustPolicy() returned error: %v", err)
}
if reflect.DeepEqual(policy, *expectedPolicy) {
t.Fatalf("GetApplicableTrustPolicy() returned unexpected policy for %s", policyName)
}
}

View File

@ -25,20 +25,23 @@ import (
"github.com/notaryproject/notation-go/internal/trustpolicy"
)
// OCIDocument represents a trustPolicy.json document for OCI artifacts
type OCIDocument struct {
// Document represents a trustpolicy.json document
type Document struct {
// Version of the policy document
Version string `json:"version"`
// TrustPolicies include each policy statement
TrustPolicies []OCITrustPolicy `json:"trustPolicies"`
TrustPolicies []TrustPolicy `json:"trustPolicies"`
}
// OCITrustPolicy represents a policy statement in the policy document for OCI artifacts
type OCITrustPolicy struct {
// TrustPolicy represents a policy statement in the policy document
type TrustPolicy struct {
// Name of the policy statement
Name string `json:"name"`
// RegistryScopes that this policy statement affects
RegistryScopes []string `json:"registryScopes"`
// SignatureVerification setting for this policy statement
SignatureVerification SignatureVerification `json:"signatureVerification"`
@ -47,80 +50,49 @@ type OCITrustPolicy struct {
// TrustedIdentities this policy statement pins
TrustedIdentities []string `json:"trustedIdentities"`
// RegistryScopes that this policy statement affects
RegistryScopes []string `json:"registryScopes"`
}
// Document represents a trustPolicy.json document
// Deprecated: Document exists for historical compatibility and should not be used.
// To create OCI Document, use OCIDocument.
type Document = OCIDocument
var supportedPolicyVersions = []string{"1.0"}
// TrustPolicy represents a policy statement in the policy document
// Deprecated: TrustPolicy exists for historical compatibility and should not be used.
// To create OCI TrustPolicy, use OCITrustPolicy.
type TrustPolicy = OCITrustPolicy
// LoadDocument loads a trust policy document from a local file system
// Deprecated: LoadDocument function exists for historical compatibility and should not be used.
// To load OCI Document, use LoadOCIDocument function.
var LoadDocument = LoadOCIDocument
var supportedOCIPolicyVersions = []string{"1.0"}
// LoadOCIDocument retrieves a trust policy document from the local file system.
// It attempts to read from dir.PathOCITrustPolicy first; if not found, it tries dir.PathTrustPolicy.
// If both dir.PathOCITrustPolicy and dir.PathTrustPolicy exist, dir.PathOCITrustPolicy will be read.
func LoadOCIDocument() (*OCIDocument, error) {
var doc OCIDocument
// attempt to load the document from dir.PathOCITrustPolicy
if err := getDocument(dir.PathOCITrustPolicy, &doc); err != nil {
// if the document is not found at the first path, try the second path
if errors.As(err, &errPolicyNotExist{}) {
if err := getDocument(dir.PathTrustPolicy, &doc); err != nil {
return nil, err
}
return &doc, nil
}
// if an error occurred other than the document not found, return it
// LoadDocument retrieves a trust policy document from the local file system.
func LoadDocument() (*Document, error) {
var doc Document
if err := getDocument(dir.PathTrustPolicy, &doc); err != nil {
return nil, err
}
return &doc, nil
}
// Validate validates a policy document according to its version's rule set.
// if any rule is violated, returns an error
func (policyDoc *OCIDocument) Validate() error {
func (policyDoc *Document) Validate() error {
// sanity check
if policyDoc == nil {
return errors.New("oci trust policy document cannot be nil")
return errors.New("trust policy document cannot be nil")
}
// Validate Version
if policyDoc.Version == "" {
return errors.New("oci trust policy document has empty version, version must be specified")
return errors.New("trust policy document has empty version, version must be specified")
}
if !slices.Contains(supportedOCIPolicyVersions, policyDoc.Version) {
return fmt.Errorf("oci trust policy document uses unsupported version %q", policyDoc.Version)
if !slices.Contains(supportedPolicyVersions, policyDoc.Version) {
return fmt.Errorf("trust policy document uses unsupported version %q", policyDoc.Version)
}
// Validate the policy according to 1.0 rules
if len(policyDoc.TrustPolicies) == 0 {
return errors.New("oci trust policy document can not have zero trust policy statements")
return errors.New("trust policy document can not have zero trust policy statements")
}
policyNames := set.New[string]()
for _, statement := range policyDoc.TrustPolicies {
// Verify unique policy statement names across the policy document
if policyNames.Contains(statement.Name) {
return fmt.Errorf("multiple oci trust policy statements use the same name %q, statement names must be unique", statement.Name)
return fmt.Errorf("multiple trust policy statements use the same name %q, statement names must be unique", statement.Name)
}
if err := validatePolicyCore(statement.Name, statement.SignatureVerification, statement.TrustStores, statement.TrustedIdentities); err != nil {
return fmt.Errorf("oci trust policy: %w", err)
return fmt.Errorf("trust policy: %w", err)
}
policyNames.Add(statement.Name)
@ -138,14 +110,14 @@ func (policyDoc *OCIDocument) Validate() error {
// statement that applies to the given registry scope. If no applicable trust
// policy is found, returns an error
// see https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#selecting-a-trust-policy-based-on-artifact-uri
func (policyDoc *OCIDocument) GetApplicableTrustPolicy(artifactReference string) (*OCITrustPolicy, error) {
func (policyDoc *Document) GetApplicableTrustPolicy(artifactReference string) (*TrustPolicy, error) {
artifactPath, err := getArtifactPathFromReference(artifactReference)
if err != nil {
return nil, err
}
var wildcardPolicy *OCITrustPolicy
var applicablePolicy *OCITrustPolicy
var wildcardPolicy *TrustPolicy
var applicablePolicy *TrustPolicy
for _, policyStatement := range policyDoc.TrustPolicies {
if slices.Contains(policyStatement.RegistryScopes, trustpolicy.Wildcard) {
// we need to deep copy because we can't use the loop variable
@ -163,13 +135,13 @@ func (policyDoc *OCIDocument) GetApplicableTrustPolicy(artifactReference string)
} else if wildcardPolicy != nil {
return wildcardPolicy, nil
} else {
return nil, fmt.Errorf("artifact %q has no applicable oci trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: %s", artifactReference, trustPolicyLink)
return nil, fmt.Errorf("artifact %q has no applicable trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: %s", artifactReference, trustPolicyLink)
}
}
// clone returns a pointer to the deeply copied TrustPolicy
func (t *OCITrustPolicy) clone() *OCITrustPolicy {
return &OCITrustPolicy{
func (t *TrustPolicy) clone() *TrustPolicy {
return &TrustPolicy{
Name: t.Name,
SignatureVerification: t.SignatureVerification,
TrustedIdentities: append([]string(nil), t.TrustedIdentities...),
@ -180,15 +152,15 @@ func (t *OCITrustPolicy) clone() *OCITrustPolicy {
// validateRegistryScopes validates if the policy document is following the
// Notary Project spec rules for registry scopes
func validateRegistryScopes(policyDoc *OCIDocument) error {
func validateRegistryScopes(policyDoc *Document) error {
registryScopeCount := make(map[string]int)
for _, statement := range policyDoc.TrustPolicies {
// Verify registry scopes are valid
if len(statement.RegistryScopes) == 0 {
return fmt.Errorf("oci trust policy statement %q has zero registry scopes, it must specify registry scopes with at least one value", statement.Name)
return fmt.Errorf("trust policy statement %q has zero registry scopes, it must specify registry scopes with at least one value", statement.Name)
}
if len(statement.RegistryScopes) > 1 && slices.Contains(statement.RegistryScopes, trustpolicy.Wildcard) {
return fmt.Errorf("oci trust policy statement %q uses wildcard registry scope '*', a wildcard scope cannot be used in conjunction with other scope values", statement.Name)
return fmt.Errorf("trust policy statement %q uses wildcard registry scope '*', a wildcard scope cannot be used in conjunction with other scope values", statement.Name)
}
for _, scope := range statement.RegistryScopes {
if scope != trustpolicy.Wildcard {
@ -203,7 +175,7 @@ func validateRegistryScopes(policyDoc *OCIDocument) error {
// Verify one policy statement per registry scope
for key := range registryScopeCount {
if registryScopeCount[key] > 1 {
return fmt.Errorf("registry scope %q is present in multiple oci trust policy statements, one registry scope value can only be associated with one statement", key)
return fmt.Errorf("registry scope %q is present in multiple trust policy statements, one registry scope value can only be associated with one statement", key)
}
}

View File

@ -23,47 +23,32 @@ import (
"github.com/notaryproject/notation-go/dir"
)
func TestLoadOCIDocumentFromOldFileLocation(t *testing.T) {
func TestLoadDocumentFromFileLocation(t *testing.T) {
tempRoot := t.TempDir()
dir.UserConfigDir = tempRoot
path := filepath.Join(tempRoot, "trustpolicy.json")
policyJson, _ := json.Marshal(dummyOCIPolicyDocument())
policyJson, _ := json.Marshal(dummyPolicyDocument())
if err := os.WriteFile(path, policyJson, 0600); err != nil {
t.Fatalf("TestLoadOCIDocument write policy file failed. Error: %v", err)
t.Fatalf("TestLoadDocument write policy file failed. Error: %v", err)
}
t.Cleanup(func() { os.RemoveAll(tempRoot) })
if _, err := LoadOCIDocument(); err != nil {
t.Fatalf("LoadOCIDocument() should not throw error for an existing policy file. Error: %v", err)
if _, err := LoadDocument(); err != nil {
t.Fatalf("LoadDocument() should not throw error for an existing policy file. Error: %v", err)
}
}
func TestLoadOCIDocumentFromNewFileLocation(t *testing.T) {
func TestLoadDocumentError(t *testing.T) {
tempRoot := t.TempDir()
dir.UserConfigDir = tempRoot
path := filepath.Join(tempRoot, "trustpolicy.oci.json")
policyJson, _ := json.Marshal(dummyOCIPolicyDocument())
if err := os.WriteFile(path, policyJson, 0600); err != nil {
t.Fatalf("TestLoadOCIDocument write policy file failed. Error: %v", err)
}
t.Cleanup(func() { os.RemoveAll(tempRoot) })
if _, err := LoadOCIDocument(); err != nil {
t.Fatalf("LoadOCIDocument() should not throw error for an existing policy file. Error: %v", err)
}
}
func TestLoadOCIDocumentError(t *testing.T) {
tempRoot := t.TempDir()
dir.UserConfigDir = tempRoot
if _, err := LoadOCIDocument(); err == nil {
t.Fatalf("LoadOCIDocument() should throw error if OCI trust policy is not found")
if _, err := LoadDocument(); err == nil {
t.Fatalf("LoadDocument() should throw error if trust policy is not found")
}
}
// TestApplicableTrustPolicy tests filtering policies against registry scopes
func TestApplicableTrustPolicy(t *testing.T) {
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyStatement := policyDoc.TrustPolicies[0]
policyStatement.Name = "test-statement-name-1"
@ -72,7 +57,7 @@ func TestApplicableTrustPolicy(t *testing.T) {
policyStatement.RegistryScopes = []string{registryScope}
policyStatement.SignatureVerification = SignatureVerification{VerificationLevel: "strict"}
policyDoc.TrustPolicies = []OCITrustPolicy{
policyDoc.TrustPolicies = []TrustPolicy{
policyStatement,
}
// existing Registry Scope
@ -83,12 +68,12 @@ func TestApplicableTrustPolicy(t *testing.T) {
// non-existing Registry Scope
policy, err = (&policyDoc).GetApplicableTrustPolicy("non.existing.scope/repo@sha256:hash")
if policy != nil || err == nil || err.Error() != "artifact \"non.existing.scope/repo@sha256:hash\" has no applicable oci trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy" {
if policy != nil || err == nil || err.Error() != "artifact \"non.existing.scope/repo@sha256:hash\" has no applicable trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy" {
t.Fatalf("GetApplicableTrustPolicy() should return nil for non existing registry scope")
}
// wildcard registry scope
wildcardStatement := OCITrustPolicy{
wildcardStatement := TrustPolicy{
Name: "test-statement-name-2",
SignatureVerification: SignatureVerification{VerificationLevel: "skip"},
TrustStores: []string{},
@ -96,7 +81,7 @@ func TestApplicableTrustPolicy(t *testing.T) {
RegistryScopes: []string{"*"},
}
policyDoc.TrustPolicies = []OCITrustPolicy{
policyDoc.TrustPolicies = []TrustPolicy{
policyStatement,
wildcardStatement,
}
@ -110,130 +95,130 @@ func TestApplicableTrustPolicy(t *testing.T) {
// and tests various validations on policy elements
func TestValidateInvalidPolicyDocument(t *testing.T) {
// Sanity check
var nilPolicyDoc *OCIDocument
var nilPolicyDoc *Document
err := nilPolicyDoc.Validate()
if err == nil || err.Error() != "oci trust policy document cannot be nil" {
if err == nil || err.Error() != "trust policy document cannot be nil" {
t.Fatalf("nil policyDoc should return error")
}
// Invalid Version
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyDoc.Version = "invalid"
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy document uses unsupported version \"invalid\"" {
if err == nil || err.Error() != "trust policy document uses unsupported version \"invalid\"" {
t.Fatalf("invalid version should return error")
}
// No Policy Statements
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies = nil
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy document can not have zero trust policy statements" {
if err == nil || err.Error() != "trust policy document can not have zero trust policy statements" {
t.Fatalf("zero policy statements should return error")
}
// No Policy Statement Name
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].Name = ""
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: a trust policy statement is missing a name, every statement requires a name" {
if err == nil || err.Error() != "trust policy: a trust policy statement is missing a name, every statement requires a name" {
t.Fatalf("policy statement with no name should return an error")
}
// No Registry Scopes
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].RegistryScopes = nil
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy statement \"test-statement-name\" has zero registry scopes, it must specify registry scopes with at least one value" {
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" has zero registry scopes, it must specify registry scopes with at least one value" {
t.Fatalf("policy statement with registry scopes should return error")
}
// Multiple policy statements with same registry scope
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyStatement1 := policyDoc.TrustPolicies[0].clone()
policyStatement2 := policyDoc.TrustPolicies[0].clone()
policyStatement2.Name = "test-statement-name-2"
policyDoc.TrustPolicies = []OCITrustPolicy{*policyStatement1, *policyStatement2}
policyDoc.TrustPolicies = []TrustPolicy{*policyStatement1, *policyStatement2}
err = policyDoc.Validate()
if err == nil || err.Error() != "registry scope \"registry.acme-rockets.io/software/net-monitor\" is present in multiple oci trust policy statements, one registry scope value can only be associated with one statement" {
if err == nil || err.Error() != "registry scope \"registry.acme-rockets.io/software/net-monitor\" is present in multiple trust policy statements, one registry scope value can only be associated with one statement" {
t.Fatalf("Policy statements with same registry scope should return error %q", err)
}
// Registry scopes with a wildcard
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].RegistryScopes = []string{"*", "registry.acme-rockets.io/software/net-monitor"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy statement \"test-statement-name\" uses wildcard registry scope '*', a wildcard scope cannot be used in conjunction with other scope values" {
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" uses wildcard registry scope '*', a wildcard scope cannot be used in conjunction with other scope values" {
t.Fatalf("policy statement with more than a wildcard registry scope should return error")
}
// Invalid SignatureVerification
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification = SignatureVerification{VerificationLevel: "invalid"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" has invalid signatureVerification: invalid signature verification level \"invalid\"" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" has invalid signatureVerification: invalid signature verification level \"invalid\"" {
t.Fatalf("policy statement with invalid SignatureVerification should return error")
}
// Invalid SignatureVerification VerifyTimestamp
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification.VerifyTimestamp = "invalid"
expectedErrMsg := "oci trust policy: trust policy statement \"test-statement-name\" has invalid signatureVerification: verifyTimestamp must be \"always\" or \"afterCertExpiry\", but got \"invalid\""
expectedErrMsg := "trust policy: trust policy statement \"test-statement-name\" has invalid signatureVerification: verifyTimestamp must be \"always\" or \"afterCertExpiry\", but got \"invalid\""
err = policyDoc.Validate()
if err == nil || err.Error() != expectedErrMsg {
t.Fatalf("expected %s, but got %s", expectedErrMsg, err)
}
// strict SignatureVerification should have a trust store
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustStores = []string{}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" is either missing trust stores or trusted identities, both must be specified" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" is either missing trust stores or trusted identities, both must be specified" {
t.Fatalf("strict SignatureVerification should have a trust store")
}
// strict SignatureVerification should have trusted identities
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustedIdentities = []string{}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" is either missing trust stores or trusted identities, both must be specified" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" is either missing trust stores or trusted identities, both must be specified" {
t.Fatalf("strict SignatureVerification should have trusted identities")
}
// skip SignatureVerification should not have trust store or trusted identities
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification = SignatureVerification{VerificationLevel: "skip"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" is set to skip signature verification but configured with trust stores and/or trusted identities, remove them if signature verification needs to be skipped" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" is set to skip signature verification but configured with trust stores and/or trusted identities, remove them if signature verification needs to be skipped" {
t.Fatalf("strict SignatureVerification should have trusted identities")
}
// Empty Trusted Identity should throw error
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustedIdentities = []string{""}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" has an empty trusted identity" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" has an empty trusted identity" {
t.Fatalf("policy statement with empty trusted identity should return error")
}
// Trusted Identity without separator should throw error
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustedIdentities = []string{"x509.subject"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" has trusted identity \"x509.subject\" missing separator" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" has trusted identity \"x509.subject\" missing separator" {
t.Fatalf("policy statement with trusted identity missing separator should return error")
}
// Empty Trusted Identity value should throw error
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustedIdentities = []string{"x509.subject:"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:\" without an identity value" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:\" without an identity value" {
t.Fatalf("policy statement with trusted identity missing identity value should return error")
}
// trust store/trusted identities are optional for skip SignatureVerification
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification = SignatureVerification{VerificationLevel: "skip"}
policyDoc.TrustPolicies[0].TrustStores = []string{}
policyDoc.TrustPolicies[0].TrustedIdentities = []string{}
@ -243,52 +228,52 @@ func TestValidateInvalidPolicyDocument(t *testing.T) {
}
// Trust Store missing separator
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustStores = []string{"ca"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" has malformed trust store value \"ca\". The required format is <TrustStoreType>:<TrustStoreName>" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" has malformed trust store value \"ca\". The required format is <TrustStoreType>:<TrustStoreName>" {
t.Fatalf("policy statement with trust store missing separator should return error")
}
// Invalid Trust Store type
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustStores = []string{"invalid:test-trust-store"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" uses an unsupported trust store type \"invalid\" in trust store value \"invalid:test-trust-store\"" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" uses an unsupported trust store type \"invalid\" in trust store value \"invalid:test-trust-store\"" {
t.Fatalf("policy statement with invalid trust store type should return error")
}
// Empty Named Store
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustStores = []string{"ca:"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" uses an unsupported trust store name \"\" in trust store value \"ca:\". Named store name needs to follow [a-zA-Z0-9_.-]+ format" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" uses an unsupported trust store name \"\" in trust store value \"ca:\". Named store name needs to follow [a-zA-Z0-9_.-]+ format" {
t.Fatalf("policy statement with trust store missing named store should return error")
}
// trusted identities with a wildcard
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyDoc.TrustPolicies[0].TrustedIdentities = []string{"*", "test-identity"}
err = policyDoc.Validate()
if err == nil || err.Error() != "oci trust policy: trust policy statement \"test-statement-name\" uses a wildcard trusted identity '*', a wildcard identity cannot be used in conjunction with other values" {
if err == nil || err.Error() != "trust policy: trust policy statement \"test-statement-name\" uses a wildcard trusted identity '*', a wildcard identity cannot be used in conjunction with other values" {
t.Fatalf("policy statement with more than a wildcard trusted identity should return error")
}
// Policy Document with duplicate policy statement names
policyDoc = dummyOCIPolicyDocument()
policyDoc = dummyPolicyDocument()
policyStatement1 = policyDoc.TrustPolicies[0].clone()
policyStatement2 = policyDoc.TrustPolicies[0].clone()
policyStatement2.RegistryScopes = []string{"registry.acme-rockets.io/software/legacy/metrics"}
policyDoc.TrustPolicies = []OCITrustPolicy{*policyStatement1, *policyStatement2}
policyDoc.TrustPolicies = []TrustPolicy{*policyStatement1, *policyStatement2}
err = policyDoc.Validate()
if err == nil || err.Error() != "multiple oci trust policy statements use the same name \"test-statement-name\", statement names must be unique" {
if err == nil || err.Error() != "multiple trust policy statements use the same name \"test-statement-name\", statement names must be unique" {
t.Fatalf("policy statements with same name should return error")
}
}
// TestValidRegistryScopes tests valid scopes are accepted
func TestValidRegistryScopes(t *testing.T) {
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
validScopes := []string{
"*", "example.com/rep", "example.com:8080/rep/rep2", "example.com/rep/subrep/subsub",
"10.10.10.10:8080/rep/rep2", "domain/rep", "domain:1234/rep",
@ -305,7 +290,7 @@ func TestValidRegistryScopes(t *testing.T) {
// TestInvalidRegistryScopes tests invalid scopes are rejected
func TestInvalidRegistryScopes(t *testing.T) {
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
invalidScopes := []string{
"", "1:1", "a,b", "abcd", "1111", "1,2", "example.com/rep:tag",
"example.com/rep/subrep/sub:latest", "example.com", "rep/rep2:latest",
@ -333,7 +318,7 @@ func TestInvalidRegistryScopes(t *testing.T) {
// TestValidateValidPolicyDocument tests a happy policy document
func TestValidateValidPolicyDocument(t *testing.T) {
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyStatement1 := policyDoc.TrustPolicies[0].clone()
@ -376,7 +361,7 @@ func TestValidateValidPolicyDocument(t *testing.T) {
policyStatement8.RegistryScopes = []string{"registry.acme-rockets.io/software/net-monitor8"}
policyStatement8.SignatureVerification.VerifyTimestamp = OptionAfterCertExpiry
policyDoc.TrustPolicies = []OCITrustPolicy{
policyDoc.TrustPolicies = []TrustPolicy{
*policyStatement1,
*policyStatement2,
*policyStatement3,

View File

@ -26,10 +26,10 @@ import (
"github.com/notaryproject/notation-go/dir"
)
func dummyOCIPolicyDocument() OCIDocument {
return OCIDocument{
func dummyPolicyDocument() Document {
return Document{
Version: "1.0",
TrustPolicies: []OCITrustPolicy{
TrustPolicies: []TrustPolicy{
{
Name: "test-statement-name",
RegistryScopes: []string{"registry.acme-rockets.io/software/net-monitor"},
@ -41,20 +41,6 @@ func dummyOCIPolicyDocument() OCIDocument {
}
}
func dummyBlobPolicyDocument() BlobDocument {
return BlobDocument{
Version: "1.0",
TrustPolicies: []BlobTrustPolicy{
{
Name: "test-statement-name",
SignatureVerification: SignatureVerification{VerificationLevel: "strict"},
TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"},
TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notary,L=Seattle,ST=WA,C=US"},
},
},
}
}
// create testcase for validatePolicyCore method
func TestValidatePolicyCore(t *testing.T) {
policyName := "test-statement-name"
@ -306,22 +292,16 @@ func TestGetDocument(t *testing.T) {
t.Skip("skipping test on Windows")
}
dir.UserConfigDir = "/"
var ociDoc OCIDocument
var blobDoc BlobDocument
var doc Document
tests := []struct {
name string
expectedDocument any
actualDocument any
}{
{
name: "valid OCI policy file",
expectedDocument: dummyOCIPolicyDocument(),
actualDocument: &ociDoc,
},
{
name: "valid Blob policy file",
expectedDocument: dummyBlobPolicyDocument(),
actualDocument: &blobDoc,
name: "valid policy file",
expectedDocument: dummyPolicyDocument(),
actualDocument: &doc,
},
}
@ -345,7 +325,7 @@ func TestGetDocument(t *testing.T) {
func TestGetDocumentErrors(t *testing.T) {
dir.UserConfigDir = "/"
t.Run("non-existing policy file", func(t *testing.T) {
var doc OCIDocument
var doc Document
if err := getDocument("blaah", &doc); err == nil || err.Error() != fmt.Sprintf("trust policy is not present. To create a trust policy, see: %s", trustPolicyLink) {
t.Fatalf("getDocument() should throw error for non existent policy")
}
@ -362,7 +342,7 @@ func TestGetDocumentErrors(t *testing.T) {
}
t.Cleanup(func() { os.RemoveAll(tempRoot) })
var doc OCIDocument
var doc Document
if err := getDocument(path, &doc); err == nil || err.Error() != fmt.Sprintf("malformed trust policy. To create a trust policy, see: %s", trustPolicyLink) {
t.Fatalf("getDocument() should throw error for invalid policy file. Error: %v", err)
}
@ -379,7 +359,7 @@ func TestGetDocumentErrors(t *testing.T) {
t.Fatalf("creation of invalid permission policy file failed. Error: %v", err)
}
expectedErrMsg := fmt.Sprintf("unable to read trust policy due to file permissions, please verify the permissions of %s", path)
var doc OCIDocument
var doc Document
if err := getDocument(path, &doc); err == nil || err.Error() != expectedErrMsg {
t.Errorf("getDocument() should throw error for a policy file with bad permissions. "+
"Expected error: '%v'qq but found '%v'", expectedErrMsg, err.Error())
@ -400,7 +380,7 @@ func TestGetDocumentErrors(t *testing.T) {
if err := os.Symlink(path, symlinkPath); err != nil {
t.Fatalf("creation of symlink for policy file failed. Error: %v", err)
}
var doc OCIDocument
var doc Document
if err := getDocument(symlinkPath, &doc); err == nil || !strings.HasPrefix(err.Error(), "trust policy is not a regular file (symlinks are not supported)") {
t.Fatalf("getDocument() should throw error for a symlink policy file. Error: %v", err)
}

View File

@ -16,7 +16,6 @@ package verifier
import (
"context"
"crypto"
"crypto/x509"
"encoding/json"
"errors"
@ -47,20 +46,12 @@ import (
"github.com/notaryproject/notation-go/verifier/truststore"
pluginframework "github.com/notaryproject/notation-plugin-framework-go/plugin"
"github.com/notaryproject/tspclient-go"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
var algorithms = map[crypto.Hash]digest.Algorithm{
crypto.SHA256: digest.SHA256,
crypto.SHA384: digest.SHA384,
crypto.SHA512: digest.SHA512,
}
// verifier implements notation.Verifier, notation.BlobVerifier and notation.verifySkipper
// verifier implements notation.Verifier and notation.verifySkipper
type verifier struct {
ociTrustPolicyDoc *trustpolicy.OCIDocument
blobTrustPolicyDoc *trustpolicy.BlobDocument
trustPolicyDoc *trustpolicy.Document
trustStore truststore.X509TrustStore
pluginManager plugin.Manager
revocationClient revocation.Revocation
@ -69,7 +60,7 @@ type verifier struct {
}
// VerifierOptions specifies additional parameters that can be set when using
// the NewVerifierWithOptions constructor
// the NewWithOptions constructor
type VerifierOptions struct {
// RevocationClient is an implementation of revocation.Revocation to use for
// verifying revocation of code signing certificate chain
@ -88,70 +79,42 @@ type VerifierOptions struct {
RevocationTimestampingValidator revocation.Validator
}
// NewOCIVerifierFromConfig returns a OCI verifier based on local file system
func NewOCIVerifierFromConfig() (*verifier, error) {
// NewFromConfig returns a verifier based on local file system.
func NewFromConfig() (notation.Verifier, error) {
// load trust policy
policyDocument, err := trustpolicy.LoadOCIDocument()
policyDocument, err := trustpolicy.LoadDocument()
if err != nil {
return nil, err
}
// load trust store
x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS())
return NewVerifier(policyDocument, nil, x509TrustStore, plugin.NewCLIManager(dir.PluginFS()))
return New(policyDocument, x509TrustStore, plugin.NewCLIManager(dir.PluginFS()))
}
// NewBlobVerifierFromConfig returns a Blob verifier based on local file system
func NewBlobVerifierFromConfig() (*verifier, error) {
// load trust policy
policyDocument, err := trustpolicy.LoadBlobDocument()
if err != nil {
return nil, err
}
// load trust store
x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS())
return NewVerifier(nil, policyDocument, x509TrustStore, plugin.NewCLIManager(dir.PluginFS()))
// New creates a new verifier given trustPolicy, trustStore and pluginManager
func New(trustPolicy *trustpolicy.Document, trustStore truststore.X509TrustStore, pluginManager plugin.Manager) (notation.Verifier, error) {
return NewWithOptions(trustPolicy, trustStore, pluginManager, VerifierOptions{})
}
// NewWithOptions creates a new verifier given ociTrustPolicy, trustStore,
// pluginManager, and VerifierOptions.
//
// Deprecated: NewWithOptions function exists for historical compatibility and should not be used.
// To create verifier, use NewVerifierWithOptions function.
func NewWithOptions(ociTrustPolicy *trustpolicy.OCIDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager, opts VerifierOptions) (notation.Verifier, error) {
return NewVerifierWithOptions(ociTrustPolicy, nil, trustStore, pluginManager, opts)
}
// NewVerifier creates a new verifier given ociTrustPolicy, trustStore and pluginManager
func NewVerifier(ociTrustPolicy *trustpolicy.OCIDocument, blobTrustPolicy *trustpolicy.BlobDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager) (*verifier, error) {
return NewVerifierWithOptions(ociTrustPolicy, blobTrustPolicy, trustStore, pluginManager, VerifierOptions{})
}
// NewVerifierWithOptions creates a new verifier given ociTrustPolicy, blobTrustPolicy,
// trustStore, pluginManager, and verifierOptions
func NewVerifierWithOptions(ociTrustPolicy *trustpolicy.OCIDocument, blobTrustPolicy *trustpolicy.BlobDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager, verifierOptions VerifierOptions) (*verifier, error) {
// NewWithOptions creates a new verifier given trustPolicy, trustStore,
// pluginManager, and verifierOptions
func NewWithOptions(trustPolicy *trustpolicy.Document, trustStore truststore.X509TrustStore, pluginManager plugin.Manager, verifierOptions VerifierOptions) (notation.Verifier, error) {
if trustStore == nil {
return nil, errors.New("trustStore cannot be nil")
}
if ociTrustPolicy == nil && blobTrustPolicy == nil {
return nil, errors.New("ociTrustPolicy and blobTrustPolicy both cannot be nil")
if trustPolicy == nil {
return nil, errors.New("trustPolicy cannot be nil")
}
if ociTrustPolicy != nil {
if err := ociTrustPolicy.Validate(); err != nil {
return nil, err
}
}
if blobTrustPolicy != nil {
if err := blobTrustPolicy.Validate(); err != nil {
return nil, err
}
if err := trustPolicy.Validate(); err != nil {
return nil, err
}
v := &verifier{
ociTrustPolicyDoc: ociTrustPolicy,
blobTrustPolicyDoc: blobTrustPolicy,
trustStore: trustStore,
pluginManager: pluginManager,
trustPolicyDoc: trustPolicy,
trustStore: trustStore,
pluginManager: pluginManager,
}
if err := v.setRevocation(verifierOptions); err != nil {
@ -160,22 +123,6 @@ func NewVerifierWithOptions(ociTrustPolicy *trustpolicy.OCIDocument, blobTrustPo
return v, nil
}
// NewFromConfig returns a OCI verifier based on local file system.
//
// Deprecated: NewFromConfig function exists for historical compatibility and should not be used.
// To create an OCI verifier, use NewOCIVerifierFromConfig function.
func NewFromConfig() (notation.Verifier, error) {
return NewOCIVerifierFromConfig()
}
// New creates a new verifier given ociTrustPolicy, trustStore and pluginManager.
//
// Deprecated: New function exists for historical compatibility and should not be used.
// To create verifier, use NewVerifier function.
func New(ociTrustPolicy *trustpolicy.OCIDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager) (notation.Verifier, error) {
return NewVerifier(ociTrustPolicy, nil, trustStore, pluginManager)
}
// setRevocation sets revocation validators of v
func (v *verifier) setRevocation(verifierOptions VerifierOptions) error {
// timestamping validator
@ -221,7 +168,7 @@ func (v *verifier) SkipVerify(ctx context.Context, opts notation.VerifierVerifyO
logger := log.GetLogger(ctx)
logger.Debugf("Check verification level against artifact %v", opts.ArtifactReference)
trustPolicy, err := v.ociTrustPolicyDoc.GetApplicableTrustPolicy(opts.ArtifactReference)
trustPolicy, err := v.trustPolicyDoc.GetApplicableTrustPolicy(opts.ArtifactReference)
if err != nil {
return false, nil, notation.ErrorNoApplicableTrustPolicy{Msg: err.Error()}
}
@ -238,87 +185,6 @@ func (v *verifier) SkipVerify(ctx context.Context, opts notation.VerifierVerifyO
return false, verificationLevel, nil
}
// VerifyBlob verifies the signature of given blob , and returns the outcome upon
// successful verification.
func (v *verifier) VerifyBlob(ctx context.Context, descGenFunc notation.BlobDescriptorGenerator, signature []byte, opts notation.BlobVerifierVerifyOptions) (*notation.VerificationOutcome, error) {
logger := log.GetLogger(ctx)
logger.Debugf("Verify signature of media type %v", opts.SignatureMediaType)
if v.blobTrustPolicyDoc == nil {
return nil, errors.New("blobTrustPolicyDoc is nil")
}
var trustPolicy *trustpolicy.BlobTrustPolicy
var err error
if opts.TrustPolicyName == "" {
trustPolicy, err = v.blobTrustPolicyDoc.GetGlobalTrustPolicy()
} else {
trustPolicy, err = v.blobTrustPolicyDoc.GetApplicableTrustPolicy(opts.TrustPolicyName)
}
if err != nil {
return nil, notation.ErrorNoApplicableTrustPolicy{Msg: err.Error()}
}
logger.Infof("Trust policy configuration: %+v", trustPolicy)
// ignore the error since we already validated the policy document
verificationLevel, _ := trustPolicy.SignatureVerification.GetVerificationLevel()
outcome := &notation.VerificationOutcome{
RawSignature: signature,
VerificationLevel: verificationLevel,
}
// verificationLevel is skip
if reflect.DeepEqual(verificationLevel, trustpolicy.LevelSkip) {
logger.Debug("Skipping signature verification")
return outcome, nil
}
err = v.processSignature(ctx, signature, opts.SignatureMediaType, trustPolicy.Name, trustPolicy.TrustedIdentities, trustPolicy.TrustStores, trustPolicy.SignatureVerification, opts.PluginConfig, outcome)
if err != nil {
outcome.Error = err
return outcome, err
}
payload := &envelope.Payload{}
err = json.Unmarshal(outcome.EnvelopeContent.Payload.Content, payload)
if err != nil {
logger.Error("Failed to unmarshal the payload content in the signature blob to envelope.Payload")
outcome.Error = err
return outcome, err
}
cryptoHash := outcome.EnvelopeContent.SignerInfo.SignatureAlgorithm.Hash()
digestAlgo, ok := algorithms[cryptoHash]
if !ok {
logger.Error("Unsupported hashing algorithm: %v", cryptoHash)
err := fmt.Errorf("unsupported hashing algorithm: %v", cryptoHash)
outcome.Error = err
return outcome, err
}
desc, err := descGenFunc(digestAlgo)
if err != nil {
errMsg := fmt.Sprintf("failed to generate descriptor for given artifact. Error: %s", err)
logger.Error(errMsg)
descErr := errors.New(errMsg)
outcome.Error = descErr
return outcome, descErr
}
if desc.Digest != payload.TargetArtifact.Digest || desc.Size != payload.TargetArtifact.Size ||
(desc.MediaType != "" && desc.MediaType != payload.TargetArtifact.MediaType) {
logger.Infof("payload present in the signature: %+v", payload.TargetArtifact)
logger.Infof("payload derived from the blob: %+v", desc)
outcome.Error = errors.New("integrity check failed. signature does not match the given blob")
}
if len(opts.UserMetadata) > 0 {
err := verifyUserMetadata(logger, payload, opts.UserMetadata)
if err != nil {
outcome.Error = err
}
}
return outcome, outcome.Error
}
// Verify verifies the signature associated the target OCI
// artifact with manifest descriptor `desc`, and returns the outcome upon
// successful verification.
@ -331,11 +197,11 @@ func (v *verifier) Verify(ctx context.Context, desc ocispec.Descriptor, signatur
logger := log.GetLogger(ctx)
logger.Debugf("Verify signature against artifact %v referenced as %s in signature media type %v", desc.Digest, artifactRef, envelopeMediaType)
if v.ociTrustPolicyDoc == nil {
return nil, errors.New("ociTrustPolicyDoc is nil")
if v.trustPolicyDoc == nil {
return nil, errors.New("trustPolicyDoc is nil")
}
trustPolicy, err := v.ociTrustPolicyDoc.GetApplicableTrustPolicy(artifactRef)
trustPolicy, err := v.trustPolicyDoc.GetApplicableTrustPolicy(artifactRef)
if err != nil {
return nil, notation.ErrorNoApplicableTrustPolicy{Msg: err.Error()}
}

View File

@ -16,12 +16,12 @@ package verifier
import (
"context"
"crypto/x509"
"encoding/pem"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"reflect"
"strconv"
"testing"
"time"
@ -32,7 +32,6 @@ import (
"github.com/notaryproject/notation-core-go/revocation/purpose"
"github.com/notaryproject/notation-core-go/signature"
_ "github.com/notaryproject/notation-core-go/signature/cose"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-core-go/testhelper"
corex509 "github.com/notaryproject/notation-core-go/x509"
"github.com/notaryproject/notation-go"
@ -43,45 +42,16 @@ import (
"github.com/notaryproject/notation-go/signer"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
var testSig = `{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJhbm5vdGF0aW9ucyI6eyJidWlsZElkIjoiMTAxIn0sImRpZ2VzdCI6InNoYTM4NDpiOGFiMjRkYWZiYTVjZjdlNGM4OWM1NjJmODExY2YxMDQ5M2Q0MjAzZGE5ODJkM2IxMzQ1ZjM2NmNhODYzZDljMmVkMzIzZGJkMGZiN2ZmODNhODAzMDJjZWZmYTVhNjEiLCJtZWRpYVR5cGUiOiJ2aWRlby9tcDQiLCJzaXplIjoxMn19","protected":"eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI0LTA0LTA0VDE1OjAzOjA2LTA3OjAwIn0","header":{"x5c":["MIIEbTCCAtWgAwIBAgICAK0wDQYJKoZIhvcNAQELBQAwZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxJTAjBgNVBAMTHE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwIBcNMjQwNDA0MjIwMzA1WhgPMjEyNDA0MDQyMjAzMDVaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MSUwIwYDVQQDExxOb3RhdGlvbiBFeGFtcGxlIHNlbGYtc2lnbmVkMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0dXD9UqzZcGlBlvPHO2uf+Sel/xwf/eOMS6Q30GV6JPeu9czLmyR0YMfC6P0N4zDzVYYZtQLkS5lalTMGX9A3yj9aXtXvtoYtLx2mF1CfdQJMcrT63wVVTWiPPe2JT8KHkkiACzVY6LTwc4s+DIAw9Gv21Uu6bFy4WWlGMp8UwTucR0JqaFoXzB6vxVRTkK8RRLM9Pj0hM5NwobpuZ+pc+ZS/7PhdvQHVzHeLLV9S7fHxw3n1c0ti8VUjSPSqCIEqOL3Eu/0pWMXB2A1xzn3RBfnzZMD3Tw3ksFgLMVzblhv41c6gr4cgjaS4wWwUvq9Xndd7Io8QNvxyiRDX5cHwQSEOmDfmegTIaLR0dKfvjY4ZJq8Y1DnaXU4RD6XeihtZykMlx7nTUyZZXpQ1akjh3VMzPykJ4mIknHh02zGRT9ZE8E1kYzRWhU/0MAzVrTTFHpric6jO459ouTnQXFjKwAcoD5+bNY6TuhC18iar7+l4BPPI1mFuqETnMfkkJQZAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAe5wyQPo+h1Yk2PkaA5aJKuU8azF2pTLfhQwAn/1XqPcmhNQuomOP0waoBsh+6sexfIDZaNuJ+zZUxqYHke/23+768SMiJCpuJfion3ak3Ka/IVNz48G0V+V+Vog+elkZzpdUQd30njLVcoQsihp0I/Gs3pnG2SeHmsdvYVuzycdYWTt5BFu4N8VWg4x4pfRMgDG7HGxRAacz2vTdqAx6rpWjO4xc0ZO8iUKjAeKHc7RuSx2dhUaRP9P8G8NBNtG6xNnbXIEjH6kP05srFRZ2jxm1an7sjsOpbBdIDztc0J+cb5yjBx7zo1OzWcmDUqMEXDR/WoygPzwhhHvWWvTqwVSEUvYnSaI6wxyHGxPFuX3+vCEZxU8NEGIuJtfYXWeo9cev5+PqjDgVu0uCWF53ZFsXNWbpff1qpG/CgrpFh3vN6uquMK9H5zaJBKr0GZFUsNRB1S8cUBgcjIZlWv3wrJQaOIFzF4RFO9dsYcG/b7ubdqSNGe4qfbsyuWf+1xsx"],"io.cncf.notary.signingAgent":"example signing agent"},"signature":"WMtF0u9GnQxJCpgrcxKZtNKNf3fvu2vnvOjd_2vQvjB4I9YKRYDQdr1q0AC0rU9b5aAGqP6Uh3jTbPkHHmOzGhXhRtidunfzOAeC6dPinR_RlnVMnVUY4cimZZG6Tg2tlgqGazgdzphnuZQpxUnK5mSInnWztXz_1-l_UJdPII49loJVE23hvWKDp8xOvMLftFXFlCYF9wE1ecTsYEAdrgB_XurFqbhhfeNcYie02aSMXfN0-ip9MHlIPhGrrOKLVm0w_S3nNBnuHHZ5lARgTm7tHtiNC0XxGCCk8qqteRZ4Vm2VM_UFMVOpdfh5KE_iTzmPCiHfNOJfgmvg5nysL1XUwGJ_KzCkPfY1Hq_4k73lia6RS6NSl1bSQ_s3uMBm3nx74WCmjK89RAihMIQ6s0PmUKQoWsIZ_5lWZ6uFW6LreoYyBFwvVVsSGSUx54-Gh76bwrt75va2VHpolSEXdhjcTK0KgscKLjU-LYDA_JD6AUaCi3WzMnpMSnO-9u_G"}`
var trustedCert = `-----BEGIN CERTIFICATE-----
MIIEbTCCAtWgAwIBAgICAK0wDQYJKoZIhvcNAQELBQAwZDELMAkGA1UEBhMCVVMx
CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3Rhcnkx
JTAjBgNVBAMTHE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwIBcNMjQwNDA0
MjIwMzA1WhgPMjEyNDA0MDQyMjAzMDVaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI
EwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MSUwIwYDVQQD
ExxOb3RhdGlvbiBFeGFtcGxlIHNlbGYtc2lnbmVkMIIBojANBgkqhkiG9w0BAQEF
AAOCAY8AMIIBigKCAYEA0dXD9UqzZcGlBlvPHO2uf+Sel/xwf/eOMS6Q30GV6JPe
u9czLmyR0YMfC6P0N4zDzVYYZtQLkS5lalTMGX9A3yj9aXtXvtoYtLx2mF1CfdQJ
McrT63wVVTWiPPe2JT8KHkkiACzVY6LTwc4s+DIAw9Gv21Uu6bFy4WWlGMp8UwTu
cR0JqaFoXzB6vxVRTkK8RRLM9Pj0hM5NwobpuZ+pc+ZS/7PhdvQHVzHeLLV9S7fH
xw3n1c0ti8VUjSPSqCIEqOL3Eu/0pWMXB2A1xzn3RBfnzZMD3Tw3ksFgLMVzblhv
41c6gr4cgjaS4wWwUvq9Xndd7Io8QNvxyiRDX5cHwQSEOmDfmegTIaLR0dKfvjY4
ZJq8Y1DnaXU4RD6XeihtZykMlx7nTUyZZXpQ1akjh3VMzPykJ4mIknHh02zGRT9Z
E8E1kYzRWhU/0MAzVrTTFHpric6jO459ouTnQXFjKwAcoD5+bNY6TuhC18iar7+l
4BPPI1mFuqETnMfkkJQZAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE
DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAe5wyQPo+h1Yk2PkaA5aJ
KuU8azF2pTLfhQwAn/1XqPcmhNQuomOP0waoBsh+6sexfIDZaNuJ+zZUxqYHke/2
3+768SMiJCpuJfion3ak3Ka/IVNz48G0V+V+Vog+elkZzpdUQd30njLVcoQsihp0
I/Gs3pnG2SeHmsdvYVuzycdYWTt5BFu4N8VWg4x4pfRMgDG7HGxRAacz2vTdqAx6
rpWjO4xc0ZO8iUKjAeKHc7RuSx2dhUaRP9P8G8NBNtG6xNnbXIEjH6kP05srFRZ2
jxm1an7sjsOpbBdIDztc0J+cb5yjBx7zo1OzWcmDUqMEXDR/WoygPzwhhHvWWvTq
wVSEUvYnSaI6wxyHGxPFuX3+vCEZxU8NEGIuJtfYXWeo9cev5+PqjDgVu0uCWF53
ZFsXNWbpff1qpG/CgrpFh3vN6uquMK9H5zaJBKr0GZFUsNRB1S8cUBgcjIZlWv3w
rJQaOIFzF4RFO9dsYcG/b7ubdqSNGe4qfbsyuWf+1xsx
-----END CERTIFICATE-----`
var ociPolicy = dummyOCIPolicyDocument()
var blobPolicy = dummyBlobPolicyDocument()
var policy = dummyPolicyDocument()
var invalidPolicy = dummyInvalidPolicyDocument()
var store = truststore.NewX509TrustStore(dir.ConfigFS())
var pm = mock.PluginManager{}
func TestNewVerifier_Error(t *testing.T) {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
_, err := New(&policyDocument, nil, nil)
expectedErr := errors.New("trustStore cannot be nil")
if err == nil || err.Error() != expectedErr.Error() {
@ -89,10 +59,31 @@ func TestNewVerifier_Error(t *testing.T) {
}
}
func TestNewFromConfig(t *testing.T) {
tempRoot := t.TempDir()
dir.UserConfigDir = tempRoot
expectedErrMsg := "trust policy is not present. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy"
_, err := NewFromConfig()
if err == nil || err.Error() != expectedErrMsg {
t.Fatalf("expected %s, but got %s", expectedErrMsg, err)
}
path := filepath.Join(tempRoot, "trustpolicy.json")
policyJson, _ := json.Marshal(dummyPolicyDocument())
if err := os.WriteFile(path, policyJson, 0600); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(tempRoot) })
_, err = NewFromConfig()
if err != nil {
t.Fatal(err)
}
}
func TestInvalidArtifactUriValidations(t *testing.T) {
verifier := verifier{
ociTrustPolicyDoc: &ociPolicy,
pluginManager: mock.PluginManager{},
trustPolicyDoc: &policy,
pluginManager: mock.PluginManager{},
}
tests := []struct {
@ -122,12 +113,12 @@ func TestInvalidArtifactUriValidations(t *testing.T) {
func TestErrorNoApplicableTrustPolicy_Error(t *testing.T) {
verifier := verifier{
ociTrustPolicyDoc: &ociPolicy,
pluginManager: mock.PluginManager{},
trustPolicyDoc: &policy,
pluginManager: mock.PluginManager{},
}
opts := notation.VerifierVerifyOptions{ArtifactReference: "non-existent-domain.com/repo@sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333"}
_, err := verifier.Verify(context.Background(), ocispec.Descriptor{}, []byte{}, opts)
if !errors.Is(err, notation.ErrorNoApplicableTrustPolicy{Msg: "artifact \"non-existent-domain.com/repo@sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333\" has no applicable oci trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy"}) {
if !errors.Is(err, notation.ErrorNoApplicableTrustPolicy{Msg: "artifact \"non-existent-domain.com/repo@sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333\" has no applicable trust policy statement. Trust policy applicability for a given artifact is determined by registryScopes. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy"}) {
t.Fatalf("no applicable trust policy must throw error")
}
}
@ -166,7 +157,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Unsupported Signature Envelope
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
expectedErr := fmt.Errorf("unable to parse the digital signature, error : signature envelope format with media type \"application/unsupported+json\" is not supported")
testCases = append(testCases, testCase{
verificationType: trustpolicy.TypeIntegrity,
@ -179,7 +170,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Integrity Success
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
testCases = append(testCases, testCase{
signatureBlob: validSigEnv,
verificationType: trustpolicy.TypeIntegrity,
@ -191,7 +182,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Integrity Failure
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
expectedErr := fmt.Errorf("signature is invalid. Error: illegal base64 data at input byte 242")
testCases = append(testCases, testCase{
signatureBlob: invalidSigEnv,
@ -205,7 +196,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Authenticity Success
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument() // trust store is configured with the root certificate of the signature by default
policyDocument := dummyPolicyDocument() // trust store is configured with the root certificate of the signature by default
testCases = append(testCases, testCase{
signatureBlob: validSigEnv,
verificationType: trustpolicy.TypeAuthenticity,
@ -217,7 +208,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Authenticity Failure
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].TrustStores = []string{"ca:valid-trust-store-2", "signingAuthority:valid-trust-store-2"} // trust store is not configured with the root certificate of the signature
expectedErr := fmt.Errorf("signature is not produced by a trusted signer")
testCases = append(testCases, testCase{
@ -232,7 +223,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Authenticity Failure with trust store missing separator
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].TrustStores = []string{"ca:valid-trust-store-2", "signingAuthority"}
expectedErr := fmt.Errorf("error while loading the trust store, trust policy statement \"test-statement-name\" is missing separator in trust store value \"signingAuthority\". The required format is <TrustStoreType>:<TrustStoreName>")
testCases = append(testCases, testCase{
@ -247,7 +238,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// TrustedIdentity Failure
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].TrustedIdentities = []string{"x509.subject:CN=LOL,O=DummyOrg,L=Hyderabad,ST=TG,C=IN"} // configure policy to not trust "CN=Notation Test Leaf Cert,O=Notary,L=Seattle,ST=WA,C=US" which is the subject of the signature's signing certificate
expectedErr := fmt.Errorf("signing certificate from the digital signature does not match the X.509 trusted identities [map[\"C\":\"IN\" \"CN\":\"LOL\" \"L\":\"Hyderabad\" \"O\":\"DummyOrg\" \"ST\":\"TG\"]] defined in the trust policy \"test-statement-name\"")
testCases = append(testCases, testCase{
@ -262,7 +253,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// TrustedIdentity Failure without separator
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].TrustedIdentities = []string{"x509.subject"}
expectedErr := fmt.Errorf("trust policy statement \"test-statement-name\" has trusted identity \"x509.subject\" missing separator")
testCases = append(testCases, testCase{
@ -277,7 +268,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// TrustedIdentity Failure with empty value
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].TrustedIdentities = []string{"x509.subject:"}
expectedErr := fmt.Errorf("trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:\" without an identity value")
testCases = append(testCases, testCase{
@ -292,7 +283,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Expiry Success
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
testCases = append(testCases, testCase{
signatureBlob: validSigEnv,
verificationType: trustpolicy.TypeExpiry,
@ -304,7 +295,7 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
// Expiry Failure
for _, level := range verificationLevels {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
expectedErr := fmt.Errorf("digital signature has expired on \"Fri, 29 Jul 2022 23:59:00 +0000\"")
testCases = append(testCases, testCase{
signatureBlob: expiredSigEnv,
@ -336,10 +327,10 @@ func assertNotationVerification(t *testing.T, scheme signature.SigningScheme) {
t.Fatalf("unexpected error while creating revocation object: %v", err)
}
verifier := verifier{
ociTrustPolicyDoc: &tt.policyDocument,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &tt.policyDocument,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
}
outcome, _ := verifier.Verify(context.Background(), ocispec.Descriptor{}, tt.signatureBlob, tt.opts)
verifyResult(outcome, expectedResult, tt.expectedErr, t)
@ -387,7 +378,7 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
t.Run("enforced revoked cert", func(t *testing.T) {
testedLevel := trustpolicy.LevelStrict
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification.VerificationLevel = testedLevel.Name
policyDoc.TrustPolicies[0].SignatureVerification.Override = map[trustpolicy.ValidationType]trustpolicy.ValidationAction{
trustpolicy.TypeAuthenticity: trustpolicy.ActionLog,
@ -403,10 +394,10 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
dir.UserConfigDir = "testdata"
verifier := verifier{
ociTrustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
}
outcome, err := verifier.Verify(context.Background(), desc, envelopeBlob, opts)
if err == nil || err.Error() != expectedErr.Error() {
@ -416,7 +407,7 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
})
t.Run("log revoked cert", func(t *testing.T) {
testedLevel := trustpolicy.LevelStrict
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification.VerificationLevel = testedLevel.Name
policyDoc.TrustPolicies[0].SignatureVerification.Override = map[trustpolicy.ValidationType]trustpolicy.ValidationAction{
trustpolicy.TypeAuthenticity: trustpolicy.ActionLog,
@ -432,10 +423,10 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
dir.UserConfigDir = "testdata"
verifier := verifier{
ociTrustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
}
ctx := context.Background()
outcome, err := verifier.Verify(ctx, desc, envelopeBlob, opts)
@ -446,7 +437,7 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
})
t.Run("skip revoked cert", func(t *testing.T) {
testedLevel := trustpolicy.LevelStrict
policyDoc := dummyOCIPolicyDocument()
policyDoc := dummyPolicyDocument()
policyDoc.TrustPolicies[0].SignatureVerification.VerificationLevel = testedLevel.Name
policyDoc.TrustPolicies[0].SignatureVerification.Override = map[trustpolicy.ValidationType]trustpolicy.ValidationAction{
trustpolicy.TypeAuthenticity: trustpolicy.ActionLog,
@ -456,10 +447,10 @@ func TestVerifyRevocationEnvelope(t *testing.T) {
dir.UserConfigDir = "testdata"
verifier := verifier{
ociTrustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDoc,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
}
outcome, err := verifier.Verify(context.Background(), desc, envelopeBlob, opts)
if err != nil {
@ -708,86 +699,56 @@ func TestVerifyRevocation(t *testing.T) {
}
func TestNew(t *testing.T) {
if _, err := New(&ociPolicy, store, pm); err != nil {
if _, err := New(&policy, store, pm); err != nil {
t.Fatalf("expected New constructor to succeed, but got %v", err)
}
}
func TestNewWithOptions(t *testing.T) {
if _, err := NewWithOptions(&ociPolicy, store, pm, VerifierOptions{}); err != nil {
if _, err := NewWithOptions(&policy, store, pm, VerifierOptions{}); err != nil {
t.Fatalf("expected NewWithOptions constructor to succeed, but got %v", err)
}
}
func TestNewVerifierWithOptions(t *testing.T) {
r, err := revocation.New(&http.Client{})
if err != nil {
t.Fatalf("unexpected error while creating revocation object: %v", err)
}
opts := VerifierOptions{RevocationClient: r}
v, err := NewVerifierWithOptions(&ociPolicy, &blobPolicy, store, pm, opts)
_, err = NewWithOptions(&policy, store, pm, opts)
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
}
if !(v.ociTrustPolicyDoc == &ociPolicy) {
t.Fatalf("expected ociTrustPolicyDoc %v, but got %v", v, v.ociTrustPolicyDoc)
}
if !(v.trustStore == store) {
t.Fatalf("expected trustStore %v, but got %v", store, v.trustStore)
}
if !reflect.DeepEqual(v.pluginManager, pm) {
t.Fatalf("expected pluginManager %v, but got %v", pm, v.pluginManager)
}
if v.revocationClient == nil {
t.Fatal("expected nonnil revocationClient")
}
if v.revocationCodeSigningValidator != nil {
t.Fatal("expected nil revocationCodeSigningValidator")
t.Fatalf("expected NewWithOptions constructor to succeed, but got %v", err)
}
_, err = NewVerifierWithOptions(nil, &blobPolicy, store, pm, opts)
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
}
_, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts)
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
}
opts.RevocationClient = nil
_, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts)
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
}
csValidator, err := revocation.NewWithOptions(revocation.Options{})
revocationCodeSigningValidator, err := revocation.NewWithOptions(revocation.Options{
OCSPHTTPClient: &http.Client{},
CertChainPurpose: purpose.CodeSigning,
})
if err != nil {
t.Fatal(err)
}
opts = VerifierOptions{
RevocationCodeSigningValidator: csValidator,
}
v, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts)
revocationTimestampingValidator, err := revocation.NewWithOptions(revocation.Options{
OCSPHTTPClient: &http.Client{},
CertChainPurpose: purpose.Timestamping,
})
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
t.Fatal(err)
}
if v.revocationCodeSigningValidator == nil {
t.Fatal("expected v.revocationCodeSigningValidator to be non-nil")
opts.RevocationCodeSigningValidator = revocationCodeSigningValidator
opts.RevocationTimestampingValidator = revocationTimestampingValidator
_, err = NewWithOptions(&policy, store, pm, opts)
if err != nil {
t.Fatalf("expected NewWithOptions constructor to succeed, but got %v", err)
}
opts = VerifierOptions{}
v, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts)
opts.RevocationClient = nil
_, err = NewWithOptions(&policy, store, pm, opts)
if err != nil {
t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err)
}
if v.revocationCodeSigningValidator == nil {
t.Fatal("expected v.revocationCodeSigningValidator to be non-nil")
t.Fatalf("expected NewWithOptions constructor to succeed, but got %v", err)
}
}
func TestNewVerifierWithOptionsError(t *testing.T) {
func TestNewWithOptionsError(t *testing.T) {
r, err := revocation.New(&http.Client{})
if err != nil {
t.Fatalf("unexpected error while creating revocation object: %v", err)
@ -804,117 +765,23 @@ func TestNewVerifierWithOptionsError(t *testing.T) {
RevocationTimestampingValidator: rt,
}
_, err = NewVerifierWithOptions(nil, nil, store, pm, opts)
if err == nil || err.Error() != "ociTrustPolicy and blobTrustPolicy both cannot be nil" {
t.Errorf("expected err but not found.")
_, err = NewWithOptions(nil, store, pm, opts)
expectedErrMsg := "trustPolicy cannot be nil"
if err == nil || err.Error() != expectedErrMsg {
t.Errorf("expected %s, but got %s", expectedErrMsg, err)
}
_, err = NewVerifierWithOptions(&ociPolicy, &blobPolicy, nil, pm, opts)
if err == nil || err.Error() != "trustStore cannot be nil" {
t.Errorf("expected err but not found.")
}
}
func TestVerifyBlob(t *testing.T) {
policy := &trustpolicy.BlobDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.BlobTrustPolicy{
{
Name: "blob-test-policy",
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"},
TrustStores: []string{"ca:dummy-ts"},
TrustedIdentities: []string{"*"},
},
},
}
v, err := NewVerifier(nil, policy, &testTrustStore{}, pm)
if err != nil {
t.Fatalf("unexpected error while creating verifier: %v", err)
_, err = NewWithOptions(&policy, nil, pm, opts)
expectedErrMsg = "trustStore cannot be nil"
if err == nil || err.Error() != expectedErrMsg {
t.Errorf("expected %s, but got %s", expectedErrMsg, err)
}
opts := notation.BlobVerifierVerifyOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
TrustPolicyName: "blob-test-policy",
_, err = NewWithOptions(&invalidPolicy, store, pm, opts)
expectedErrMsg = "trust policy document has empty version, version must be specified"
if err == nil || err.Error() != expectedErrMsg {
t.Errorf("expected %s, but got %s", expectedErrMsg, err)
}
descGenFunc := getTestDescGenFunc(false, "")
t.Run("without user defined metadata", func(t *testing.T) {
// verify with
if _, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts); err != nil {
t.Fatalf("VerifyBlob() returned unexpected error: %v", err)
}
})
t.Run("with user defined metadata", func(t *testing.T) {
opts.UserMetadata = map[string]string{"buildId": "101"}
if _, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts); err != nil {
t.Fatalf("VerifyBlob() with user metadata returned unexpected error: %v", err)
}
})
t.Run("trust policy set to skip", func(t *testing.T) {
policy.TrustPolicies[0].SignatureVerification = trustpolicy.SignatureVerification{VerificationLevel: "skip"}
opts.UserMetadata = map[string]string{"buildId": "101"}
if _, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts); err != nil {
t.Fatalf("VerifyBlob() with user metadata returned unexpected error: %v", err)
}
})
}
func TestVerifyBlob_Error(t *testing.T) {
policy := &trustpolicy.BlobDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.BlobTrustPolicy{
{
Name: "blob-test-policy",
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"},
TrustStores: []string{"ca:dummy-ts"},
TrustedIdentities: []string{"*"},
},
},
}
v, err := NewVerifier(nil, policy, &testTrustStore{}, pm)
if err != nil {
t.Fatalf("unexpected error while creating verifier: %v", err)
}
opts := notation.BlobVerifierVerifyOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
TrustPolicyName: "blob-test-policy",
}
t.Run("BlobDescriptorGenerator returns error", func(t *testing.T) {
descGenFunc := getTestDescGenFunc(true, "")
_, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts)
if err == nil || err.Error() != "failed to generate descriptor for given artifact. Error: intentional test desc generation error" {
t.Errorf("VerifyBlob() didn't return error or didnt returned expected error: %v", err)
}
})
t.Run("descriptor mismatch returns error", func(t *testing.T) {
descGenFunc := getTestDescGenFunc(false, "sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a62")
_, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts)
if err == nil || err.Error() != "integrity check failed. signature does not match the given blob" {
t.Errorf("VerifyBlob() didn't return error or didnt returned expected error: %v", err)
}
})
t.Run("signature malformed returns error", func(t *testing.T) {
descGenFunc := getTestDescGenFunc(false, "")
_, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(""), opts)
if err == nil || err.Error() != "unable to parse the digital signature, error : unexpected end of JSON input" {
t.Errorf("VerifyBlob() didn't return error or didnt returned expected error: %v", err)
}
})
t.Run("user defined metadata mismatch returns error", func(t *testing.T) {
descGenFunc := getTestDescGenFunc(false, "")
opts.UserMetadata = map[string]string{"buildId": "zzz"}
_, err = v.VerifyBlob(context.Background(), descGenFunc, []byte(testSig), opts)
if err == nil || err.Error() != "unable to find specified metadata in the signature" {
t.Fatalf("VerifyBlob() with user metadata returned unexpected error: %v", err)
}
})
}
func TestVerificationPluginInteractions(t *testing.T) {
@ -930,7 +797,7 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
pluginSigEnv = mock.MockSaPluginSigEnv
}
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
dir.UserConfigDir = "testdata"
x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS())
@ -943,10 +810,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
t.Fatalf("unexpected error while creating revocation object: %v", err)
}
v := verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts := notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err := v.Verify(context.Background(), ocispec.Descriptor{}, pluginSigEnv, opts)
@ -959,10 +826,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
pluginManager.PluginCapabilities = []proto.Capability{proto.CapabilitySignatureGenerator}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), ocispec.Descriptor{}, pluginSigEnv, opts)
@ -983,10 +850,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), mock.ImageDescriptor, pluginSigEnv, opts)
@ -1008,10 +875,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), ocispec.Descriptor{}, pluginSigEnv, opts)
@ -1032,10 +899,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), mock.ImageDescriptor, pluginSigEnv, opts)
@ -1057,10 +924,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), ocispec.Descriptor{}, pluginSigEnv, opts)
@ -1084,10 +951,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), mock.ImageDescriptor, pluginSigEnv, opts)
@ -1102,10 +969,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
pluginManager.PluginRunnerExecuteError = errors.New("revocation plugin should not be invoked when the trust policy skips revocation check")
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
trustPolicy, err := (&policyDocument).GetApplicableTrustPolicy(opts.ArtifactReference)
@ -1129,10 +996,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
pluginManager.PluginRunnerExecuteError = errors.New("invalid plugin response")
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
trustPolicy, err = (&policyDocument).GetApplicableTrustPolicy(opts.ArtifactReference)
@ -1162,10 +1029,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), mock.ImageDescriptor, pluginSigEnv, opts)
@ -1182,10 +1049,10 @@ func assertPluginVerification(scheme signature.SigningScheme, t *testing.T) {
}
v = verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts = notation.VerifierVerifyOptions{ArtifactReference: mock.SampleArtifactUri, SignatureMediaType: "application/jose+json"}
outcome, err = v.Verify(context.Background(), mock.ImageDescriptor, pluginSigEnv, opts)
@ -1233,7 +1100,7 @@ func TestVerifyX509TrustedIdentities(t *testing.T) {
}
func TestVerifyUserMetadata(t *testing.T) {
policyDocument := dummyOCIPolicyDocument()
policyDocument := dummyPolicyDocument()
policyDocument.TrustPolicies[0].SignatureVerification.VerificationLevel = trustpolicy.LevelAudit.Name
pluginManager := mock.PluginManager{}
@ -1244,10 +1111,10 @@ func TestVerifyUserMetadata(t *testing.T) {
t.Fatalf("unexpected error while creating revocation object: %v", err)
}
verifier := verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: truststore.NewX509TrustStore(dir.ConfigFS()),
pluginManager: pluginManager,
revocationClient: revocationClient,
}
tests := []struct {
@ -1313,10 +1180,10 @@ func TestPluginVersionCompatibility(t *testing.T) {
t.Fatalf("unexpected error while creating revocation object: %v", err)
}
v := verifier{
ociTrustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
trustPolicyDoc: &policyDocument,
trustStore: x509TrustStore,
pluginManager: pluginManager,
revocationClient: revocationClient,
}
opts := notation.VerifierVerifyOptions{ArtifactReference: "localhost:5000/net-monitor@sha256:fe7e9333395060c2f5e63cf36a38fba10176f183b4163a5794e081a480abba5f", SignatureMediaType: "application/jose+json"}
@ -1390,32 +1257,3 @@ func verifyResult(outcome *notation.VerificationOutcome, expectedResult notation
t.Fatalf("assertion failed. expected : %v got : %v", expectedErr, outcome.Error)
}
}
// testTrustStore implements truststore.X509TrustStore and returns the trusted certificates for a given trust-store.
type testTrustStore struct{}
func (ts *testTrustStore) GetCertificates(_ context.Context, _ truststore.Type, _ string) ([]*x509.Certificate, error) {
block, _ := pem.Decode([]byte(trustedCert))
cert, _ := x509.ParseCertificate(block.Bytes)
return []*x509.Certificate{cert}, nil
}
func getTestDescGenFunc(returnErr bool, customDigest digest.Digest) notation.BlobDescriptorGenerator {
return func(digest.Algorithm) (ocispec.Descriptor, error) {
var err error = nil
if returnErr {
err = errors.New("intentional test desc generation error")
}
var expDigest digest.Digest = "sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a61"
if customDigest != "" {
expDigest = customDigest
}
return ocispec.Descriptor{
MediaType: "video/mp4",
Digest: expDigest,
Size: 12,
}, err
}
}