Skip to content

Commit

Permalink
refactor: refactor pluginSigner to support new signature interface (#131
Browse files Browse the repository at this point in the history
)

## What
Refactor `notation-go` to support multiple envelope types.
Background can be checked in
notaryproject/notation#278
I wthe whole PR into two PRs to help review, this is the first PR. More
unit test cases will be added in the next PR.
The whole picture is here
#146

## Major Changes
- Use package `github.com/notaryproject/notation-core-go/signature` to
sign and verify.
- Combine `runner` and `signer` into a `provider` for `pluginSigner` to
sign and remove the `pluginSigProvider`.
- Add `builtinProvider` to support local signing and `externalProvider`
to support signing by plugin.
- Move the payload media type and its checks to `signature` package as
mentioned in notaryproject/notation-core-go#73
- Support new
[keySpec](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection)
and plugin contract.
- Get verification plugin and version from extended attributes.
- Add `SpeculateSignatureEnvelopeFormat` to inspect signature (This
function may change later to better inspect a signature)
- Add sign/verify from file test cases.
Signed-off-by: zaihaoyin <[email protected]>

Signed-off-by: zaihaoyin <[email protected]>
Co-authored-by: zaihaoyin <[email protected]>
  • Loading branch information
chloeyin and zaihaoyin authored Sep 27, 2022
1 parent f89d6cf commit 2bcfd34
Show file tree
Hide file tree
Showing 21 changed files with 1,668 additions and 647 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ go 1.18

require (
github.com/go-ldap/ldap/v3 v3.4.4
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/notaryproject/notation-core-go v0.1.0-alpha.3
github.com/notaryproject/notation-core-go v0.1.0-alpha.3.0.20220921054535-009c09a9628e
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2
github.com/oras-project/artifacts-spec v1.0.0-rc.2
Expand All @@ -15,6 +14,7 @@ require (
require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/notaryproject/notation-core-go v0.1.0-alpha.3 h1:gzB+h5TGzuocWiJxuYZgE/FwUIbJyKAHfk2hWSBbCGg=
github.com/notaryproject/notation-core-go v0.1.0-alpha.3/go.mod h1:Wfyh5SrQ718JegKPhTs7y74rXg86tWd5NfOx2uHK1nI=
github.com/notaryproject/notation-core-go v0.1.0-alpha.3.0.20220921054535-009c09a9628e h1:n3wJRhIVbEGg497rtKV3IMaZJv2hFKYHCOtNIOAyLYw=
github.com/notaryproject/notation-core-go v0.1.0-alpha.3.0.20220921054535-009c09a9628e/go.mod h1:mM4M9wPdu0CGgh8f3wOcu0XMiXwEKWQurjBG4nmqQ4g=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 h1:Oumw+lPnO8qNLTY2mrqPJZMoGExLi/0h/DdikoLTXVU=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86/go.mod h1:aA4vdXRS8E1TG7pLZOz85InHi3BiPdErh8IpJN6E0x4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
Expand Down
8 changes: 3 additions & 5 deletions internal/mock/mocks.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package mock

import (
"context"
_ "embed"
nsigner "github.com/notaryproject/notation-core-go/signer"
)

import (
"context"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/plugin"
"github.com/notaryproject/notation-go/plugin/manager"
Expand Down Expand Up @@ -54,7 +52,7 @@ var (
Size: 100,
Annotations: Annotations,
}
PluginExtendedCriticalAttribute = nsigner.Attribute{
PluginExtendedCriticalAttribute = signature.Attribute{
Key: "SomeKey",
Critical: true,
Value: "SomeValue",
Expand Down
14 changes: 11 additions & 3 deletions notation.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"github.com/opencontainers/go-digest"
)

// Media type for Notary payload for OCI artifacts, which contains an artifact descriptor.
const MediaTypePayload = "application/vnd.cncf.notary.payload.v1+json"
// MediaTypePayloadV1 is the supported content type for signature's payload.
const MediaTypePayloadV1 = "application/vnd.cncf.notary.payload.v1+json"

// SigningAgent is the unprotected header field used by signature.
var SigningAgent = "Notation/1.0.0"

// Descriptor describes the artifact that needs to be signed.
type Descriptor struct {
Expand Down Expand Up @@ -65,7 +68,12 @@ type Signer interface {
}

// VerifyOptions contains parameters for Verifier.Verify.
type VerifyOptions struct{}
type VerifyOptions struct {
// SignatureMediaType is the envelope type of the signature.
// Currently only `application/jose+json`` is supported.
// TODO: in the future, application/cose will also be supported.
SignatureMediaType string
}

// Validate does basic validation on VerifyOptions.
func (opts VerifyOptions) Validate() error {
Expand Down
156 changes: 156 additions & 0 deletions plugin/algorithm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package plugin

import (
"errors"

"github.com/notaryproject/notation-core-go/signature"
)

// one of the following supported key spec names.
//
// https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
const (
RSA_2048 = "RSA-2048"
RSA_3072 = "RSA-3072"
RSA_4096 = "RSA-4096"
EC_256 = "EC-256"
EC_384 = "EC-384"
EC_521 = "EC-521"
)

// one of the following supported hash algorithm names.
//
// https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
const (
SHA_256 = "SHA-256"
SHA_384 = "SHA-384"
SHA_512 = "SHA-512"
)

// one of the following supported signing algorithm names.
//
// https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
const (
ECDSA_SHA_256 = "ECDSA-SHA-256"
ECDSA_SHA_384 = "ECDSA-SHA-384"
ECDSA_SHA_512 = "ECDSA-SHA-512"
RSASSA_PSS_SHA_256 = "RSASSA-PSS-SHA-256"
RSASSA_PSS_SHA_384 = "RSASSA-PSS-SHA-384"
RSASSA_PSS_SHA_512 = "RSASSA-PSS-SHA-512"
)

// KeySpecName returns the name of a keySpec according to the spec.
func KeySpecString(k signature.KeySpec) string {
switch k.Type {
case signature.KeyTypeEC:
switch k.Size {
case 256:
return EC_256
case 384:
return EC_384
case 521:
return EC_521
}
case signature.KeyTypeRSA:
switch k.Size {
case 2048:
return RSA_2048
case 3072:
return RSA_3072
case 4096:
return RSA_4096
}
}
return ""
}

// KeySpecHashName returns the name of hash function according to the spec.
func KeySpecHashString(k signature.KeySpec) string {
switch k.Type {
case signature.KeyTypeEC:
switch k.Size {
case 256:
return SHA_256
case 384:
return SHA_384
case 521:
return SHA_512
}
case signature.KeyTypeRSA:
switch k.Size {
case 2048:
return SHA_256
case 3072:
return SHA_384
case 4096:
return SHA_512
}
}
return ""
}

// ParseKeySpecFromName parses keySpec name to a signature.keySpec type.
func ParseKeySpec(raw string) (keySpec signature.KeySpec, err error) {
switch raw {
case RSA_2048:
keySpec.Size = 2048
keySpec.Type = signature.KeyTypeRSA
case RSA_3072:
keySpec.Size = 3072
keySpec.Type = signature.KeyTypeRSA
case RSA_4096:
keySpec.Size = 4096
keySpec.Type = signature.KeyTypeRSA
case EC_256:
keySpec.Size = 256
keySpec.Type = signature.KeyTypeEC
case EC_384:
keySpec.Size = 384
keySpec.Type = signature.KeyTypeEC
case EC_521:
keySpec.Size = 521
keySpec.Type = signature.KeyTypeEC
default:
keySpec = signature.KeySpec{}
err = errors.New("unknown key spec")
}
return
}

// SigningAlgorithmName returns the signing algorithm name of an algorithm according to the spec.
func SigningAlgorithmString(alg signature.Algorithm) string {
switch alg {
case signature.AlgorithmES256:
return ECDSA_SHA_256
case signature.AlgorithmES384:
return ECDSA_SHA_384
case signature.AlgorithmES512:
return ECDSA_SHA_512
case signature.AlgorithmPS256:
return RSASSA_PSS_SHA_256
case signature.AlgorithmPS384:
return RSASSA_PSS_SHA_384
case signature.AlgorithmPS512:
return RSASSA_PSS_SHA_512
}
return ""
}

// ParseSigningAlgorithFromName parses the signing algorithm name from a given string.
func ParseSigningAlgorithm(raw string) (signature.Algorithm, error) {
switch raw {
case ECDSA_SHA_256:
return signature.AlgorithmES256, nil
case ECDSA_SHA_384:
return signature.AlgorithmES384, nil
case ECDSA_SHA_512:
return signature.AlgorithmES512, nil
case RSASSA_PSS_SHA_256:
return signature.AlgorithmPS256, nil
case RSASSA_PSS_SHA_384:
return signature.AlgorithmPS384, nil
case RSASSA_PSS_SHA_512:
return signature.AlgorithmPS512, nil
}
return 0, errors.New("unknown signing algorithm")
}
22 changes: 10 additions & 12 deletions plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package plugin
import (
"context"
"time"

"github.com/notaryproject/notation-core-go/signer"
)

// Prefix is the prefix required on all plugin binary names.
Expand Down Expand Up @@ -127,17 +125,17 @@ type DescribeKeyResponse struct {

// One of following supported key types:
// https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
KeySpec signer.KeySpec `json:"keySpec"`
KeySpec string `json:"keySpec"`
}

// GenerateSignatureRequest contains the parameters passed in a generate-signature request.
type GenerateSignatureRequest struct {
ContractVersion string `json:"contractVersion"`
KeyID string `json:"keyId"`
KeySpec signer.KeySpec `json:"keySpec"`
Hash signer.HashAlgorithm `json:"hashAlgorithm"`
Payload []byte `json:"payload"`
PluginConfig map[string]string `json:"pluginConfig,omitempty"`
ContractVersion string `json:"contractVersion"`
KeyID string `json:"keyId"`
KeySpec string `json:"keySpec"`
Hash string `json:"hashAlgorithm"`
Payload []byte `json:"payload"`
PluginConfig map[string]string `json:"pluginConfig,omitempty"`
}

func (GenerateSignatureRequest) Command() Command {
Expand All @@ -146,9 +144,9 @@ func (GenerateSignatureRequest) Command() Command {

// GenerateSignatureResponse is the response of a generate-signature request.
type GenerateSignatureResponse struct {
KeyID string `json:"keyId"`
Signature []byte `json:"signature"`
SigningAlgorithm signer.SignatureAlgorithm `json:"signingAlgorithm"`
KeyID string `json:"keyId"`
Signature []byte `json:"signature"`
SigningAlgorithm string `json:"signingAlgorithm"`

// Ordered list of certificates starting with leaf certificate
// and ending with root certificate.
Expand Down
17 changes: 17 additions & 0 deletions signature/envelope.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package signature

import (
"errors"

"github.com/notaryproject/notation-core-go/signature"
)

// ValidateEnvelopeMediaType validetes envelope media type is supported by notation-core-go.
func ValidateEnvelopeMediaType(mediaType string) error {
for _, types := range signature.RegisteredEnvelopeTypes() {
if mediaType == types {
return nil
}
}
return errors.New("invalid envelope media type")
}
46 changes: 46 additions & 0 deletions signature/envelope_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package signature

import (
"errors"
"testing"

"github.com/notaryproject/notation-core-go/signature/jws"
)

const invalidMediaType = "invalid"

func checkErrorEqual(expected, got error) bool {
if expected == nil && got == nil {
return true
}
if expected != nil && got != nil {
return expected.Error() == got.Error()
}
return false
}

func TestValidateEnvelopeMediaType(t *testing.T) {
tests := []struct {
name string
mediaType string
expectedErr error
}{
{
name: "jws signature media type",
mediaType: jws.MediaTypeEnvelope,
expectedErr: nil,
},
{
name: "invalid media type",
mediaType: invalidMediaType,
expectedErr: errors.New("invalid envelope media type"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateEnvelopeMediaType(tt.mediaType); !checkErrorEqual(tt.expectedErr, err) {
t.Fatalf("expected validate envelope media type err: %v, got: %v", tt.expectedErr, err)
}
})
}
}
Loading

0 comments on commit 2bcfd34

Please sign in to comment.