notaryproject / notation-go

A collection of libraries for supporting sign and verify OCI artifacts. Based on Notary Project specifications.
Apache License 2.0
39 stars 42 forks source link

application/jose+json is not supported #353

Closed phbelitz closed 1 year ago

phbelitz commented 1 year ago

What is not working as expected?

After integrating notation-go into a Kubernetes admission controller and trying to verify the image ghcr.io/deislabs/ratify/notary-image:signed, verification fails with unable to parse the digital signature, error : signature envelope format with media type \"application/jose+json\" is not supported.

What did you expect to happen?

notation-go should support signature formats it itself created and successfully validate the signature of ghcr.io/deislabs/ratify/notary-image:signed.

How can we reproduce it?

The image ghcr.io/deislabs/ratify/notary-image:signed was used and for verification the following certificate:

-----BEGIN CERTIFICATE-----
MIIDQzCCAiugAwIBAgIUDxHQ9JxxmnrLWTA5rAtIZCzY8mMwDQYJKoZIhvcNAQEL
BQAwKTEPMA0GA1UECgwGUmF0aWZ5MRYwFAYDVQQDDA1SYXRpZnkgU2FtcGxlMB4X
DTIzMDYyOTA1MjgzMloXDTMzMDYyNjA1MjgzMlowKTEPMA0GA1UECgwGUmF0aWZ5
MRYwFAYDVQQDDA1SYXRpZnkgU2FtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAshmsL2VM9ojhgTVUUuEsZro9jfI27VKZJ4naWSHJihmOki7IoZS8
3/3ATpkE1lGbduJ77M9UxQbEW1PnESB0bWtMQtjIbser3mFCn15yz4nBXiTIu/K4
FYv6HVdc6/cds3jgfEFNw/8RVMBUGNUiSEWa1lV1zDM2v/8GekUr6SNvMyqtY8oo
ItwxfUvlhgMNlLgd96mVnnPVLmPkCmXFN9iBMhSce6sn6P9oDIB+pr1ZpE4F5bwa
gRBg2tWN3Tz9H/z2a51Xbn7hCT5OLBRlkorHJl2HKKRoXz1hBgR8xOL+zRySH9Qo
3yx6WvluYDNfVbCREzKJf9fFiQeVe0EJOwIDAQABo2MwYTAdBgNVHQ4EFgQUKzci
EKCDwPBn4I1YZ+sDdnxEir4wHwYDVR0jBBgwFoAUKzciEKCDwPBn4I1YZ+sDdnxE
ir4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQEL
BQADggEBAGh6duwc1MvV+PUYvIkDfgj158KtYX+bv4PmcV/aemQUoArqM1ECYFjt
BlBVmTRJA0lijU5I0oZje80zW7P8M8pra0BM6x3cPnh/oZGrsuMizd4h5b5TnwuJ
hRvKFFUVeHn9kORbyQwRQ5SpL8cRGyYp+T6ncEmo0jdIOM5dgfdhwHgb+i3TejcF
90sUs65zovUjv1wa11SqOdu12cCj/MYp+H8j2lpaLL2t0cbFJlBY6DNJgxr5qync
cz8gbXrZmNbzC7W5QK5J7fcx6tlffOpt5cm427f9NiK2tira50HU7gC3HJkbiSTp
Xw10iXXMZzSbQ0/Hj2BF4B40WfAkgRg=
-----END CERTIFICATE-----

Here a shortened version of the code I used (took away lots of the error handling and surrounding logic):

type NotaryV2Validator struct {
    Name        string
    Type        string
    Trust_store truststore.X509TrustStore
}

func (nv2v *NotaryV2Validator) ValidateImage(
    ctx context.Context,
    image name.Reference,
    args policy.RuleOptions,
) (string, error) {
    img, tag := transformImage(image)

    policy, _ := nv2v.setUpTrustPolicy(img, args)
    ver, _ := verifier.New(policy, nv2v.Trust_store, nil)
    remoteRepo, _ := remote.NewRepository(img + tag)
    remoteRegistry := registry.NewRepository(remoteRepo)

    desc, _ := remoteRegistry.Resolve(nv2_ctx, img+tag)
    exampleVerifyOptions := notation.VerifyOptions{
        ArtifactReference:    fmt.Sprintf("%s@%s", img, desc.Digest.String()),
        MaxSignatureAttempts: 50,
    }

    digest, _, err := notation.Verify(nv2_ctx, ver, remoteRegistry, exampleVerifyOptions)
    if err != nil {
        return "", fmt.Errorf("failed to verify image: %s", err)
    }

    return string(digest.Digest), nil
}

func (nv2v *NotaryV2Validator) setUpTrustPolicy(
    image string,
    args policy.RuleOptions,
) (*trustpolicy.Document, error) {
    // imtr stores the certificates to be used for verification
    imtr := nv2v.Trust_store.(*InMemoryTrustStore)
    // selects all the certificates to use, by an overarching policy
    trs, _ := common.GetTrustRoots(args.TrustRoots, imtr.trust_roots, true)

    return &trustpolicy.Document{
        Version: "1.0",
        TrustPolicies: []trustpolicy.TrustPolicy{
            {
                Name:           "default",
                RegistryScopes: []string{image},
                SignatureVerification: trustpolicy.SignatureVerification{
                    VerificationLevel: trustpolicy.LevelStrict.Name,
                },
                TrustStores: utils.Map[common.TrustRoot, string](
                    trs,
                    func(tr common.TrustRoot) string {
                        return fmt.Sprintf("ca:%s", tr.Name)
                    },
                ),
                TrustedIdentities: []string{"*"},
            },
        },
    }, nil
}

Describe your environment

Running inside a distroless/base-debian11 container. The code was compiled to a binary using Go 1.19. The container itself is running inside a Kubernetes cluster as an admission controller.

What is the version of your notation-go Library?

github.com/notaryproject/notation-go v1.0.0

Two-Hearts commented 1 year ago

@phbelitz I'm trying to troubleshoot the issue, could you try adding a single line in your code under import: _ "github.com/notaryproject/notation-core-go/signature/jws". (Note the underscore _ at the front of the import, since you are not directly using jws package, it won't stand without this underscore.) Let's see if this could fix the issue. Thanks.

phbelitz commented 1 year ago

@Two-Hearts yes, awesome. I guess i should import the _ "github.com/notaryproject/notation-core-go/signature/cose" aswell? Wasn't aware that those are necessary. Closing this as the problem was fixed.

Two-Hearts commented 1 year ago

I guess i should import the _ "github.com/notaryproject/notation-core-go/signature/cose" aswell?

@phbelitz Yes, since we support two signature formats at the moment. You should also import COSE as well.