Use a Factory to create signature verifiers at policy evaluation time (#165)

* Make verifiers composable

* fix: remove unused code and improve signature verification logic

* fix: simplify abstractions and renamed some things

* fix: improve tl interface.

* fix: sort out signer/verifier
This commit is contained in:
James Carnegie
2024-09-18 13:34:10 +01:00
committed by GitHub
parent 5335a56da1
commit 05caa959c4
32 changed files with 892 additions and 535 deletions

View File

@@ -20,8 +20,5 @@ func GetAWSSigner(ctx context.Context, keyARN string, region string) (dsse.Signe
if err != nil {
return nil, fmt.Errorf("error getting aws crypto signer: %w", err)
}
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil
return NewECDSASignerVerifier(cs)
}

View File

@@ -9,52 +9,17 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"github.com/docker/attest/internal/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ECDSA256SignerVerifier struct {
crypto.Signer
}
// implement keyid function.
func (s *ECDSA256SignerVerifier) KeyID() (string, error) {
keyid, err := KeyID(s.Signer.Public())
if err != nil {
return "", fmt.Errorf("error getting keyid: %w", err)
}
return keyid, nil
}
func (s *ECDSA256SignerVerifier) Public() crypto.PublicKey {
return s.Signer.Public()
}
func (s *ECDSA256SignerVerifier) Sign(_ context.Context, data []byte) ([]byte, error) {
return s.Signer.Sign(rand.Reader, data, crypto.SHA256)
}
func (s *ECDSA256SignerVerifier) Verify(_ context.Context, data []byte, sig []byte) error {
pub, ok := s.Signer.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("public key is not ecdsa")
}
ok = ecdsa.VerifyASN1(pub, util.SHA256(data), sig)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func LoadKeyPair(priv []byte) (dsse.SignerVerifier, error) {
privateKey, err := parsePriv(priv)
if err != nil {
return nil, err
}
return &ECDSA256SignerVerifier{
Signer: privateKey,
}, nil
return NewECDSASignerVerifier(privateKey)
}
func parsePriv(privkeyBytes []byte) (*ecdsa.PrivateKey, error) {
@@ -78,7 +43,26 @@ func GenKeyPair() (dsse.SignerVerifier, error) {
if err != nil {
return nil, err
}
return &ECDSA256SignerVerifier{
Signer: signer,
}, nil
return NewECDSASignerVerifier(signer)
}
// ensure it implements crypto.Signer.
var _ crypto.Signer = (*cryptoSignerWrapper)(nil)
type cryptoSignerWrapper struct {
sv dsse.SignerVerifier
}
// Public implements crypto.Signer.
func (c *cryptoSignerWrapper) Public() crypto.PublicKey {
return c.sv.Public()
}
// Sign implements crypto.Signer.
func (c *cryptoSignerWrapper) Sign(_ io.Reader, digest []byte, _ crypto.SignerOpts) (signature []byte, err error) {
return c.sv.Sign(context.Background(), digest)
}
func AsCryptoSigner(signer dsse.SignerVerifier) (crypto.Signer, error) {
return &cryptoSignerWrapper{sv: signer}, nil
}

80
signerverifier/ecdsa.go Normal file
View File

@@ -0,0 +1,80 @@
package signerverifier
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ecdsaVerifier struct {
publicKey *ecdsa.PublicKey
keyID string
}
// ensure ECDSAVerifier implements dsse.Verifier.
var _ dsse.Verifier = (*ecdsaVerifier)(nil)
func NewECDSAVerifier(publicKey crypto.PublicKey) (dsse.Verifier, error) {
ecdsaPublicKey, ok := (publicKey).(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not an ECDSA public key")
}
return &ecdsaVerifier{
publicKey: ecdsaPublicKey,
}, nil
}
func (v *ecdsaVerifier) Verify(_ context.Context, data, signature []byte) error {
// verify payload ecdsa signature
ok := ecdsa.VerifyASN1(v.publicKey, util.SHA256(data), signature)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func (v *ecdsaVerifier) Public() crypto.PublicKey {
return v.publicKey
}
func (v *ecdsaVerifier) KeyID() (string, error) {
if v.keyID != "" {
return v.keyID, nil
}
keyID, err := KeyID(v.publicKey)
if err != nil {
return "", fmt.Errorf("failed to get key ID: %w", err)
}
v.keyID = keyID
return v.keyID, nil
}
// must implement dsse.SignerVerifier interface.
var _ dsse.SignerVerifier = (*ecdsa256SignerVerifier)(nil)
type ecdsa256SignerVerifier struct {
signer crypto.Signer
dsse.Verifier
}
func NewECDSASignerVerifier(signer crypto.Signer) (dsse.SignerVerifier, error) {
verifier, err := NewECDSAVerifier(signer.Public())
if err != nil {
return nil, fmt.Errorf("failed to create verifier: %w", err)
}
sv := &ecdsa256SignerVerifier{
signer: signer,
Verifier: verifier,
}
return sv, nil
}
func (s *ecdsa256SignerVerifier) Sign(_ context.Context, data []byte) ([]byte, error) {
return s.signer.Sign(rand.Reader, data, crypto.SHA256)
}

View File

@@ -21,8 +21,5 @@ func GetGCPSigner(ctx context.Context, reference string, opts ...option.ClientOp
if err != nil {
return nil, fmt.Errorf("error getting gcp crypto signer: %w", err)
}
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil
return NewECDSASignerVerifier(cs)
}

View File

@@ -40,6 +40,14 @@ func TestGCPKMS_Signer(t *testing.T) {
publicKey, err := ParsePublicKey([]byte(publicKeyPEM))
require.NoError(t, err)
// verify payload ecdsa signature
ok := ecdsa.VerifyASN1(publicKey, hash, sig)
ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
t.Fatal("Failed to convert publicKey to *ecdsa.PublicKey")
}
ok = ecdsa.VerifyASN1(ecdsaPublicKey, hash, sig)
assert.True(t, ok)
err = signer.Verify(ctx, msg, sig)
require.NoError(t, err)
}

View File

@@ -1,6 +1,7 @@
package signerverifier
import (
"crypto"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
@@ -9,7 +10,7 @@ import (
const pemType = "PUBLIC KEY"
func ParsePublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
func ParsePublicKey(pubkeyBytes []byte) (crypto.PublicKey, error) {
p, _ := pem.Decode(pubkeyBytes)
if p == nil {
return nil, fmt.Errorf("pubkey file does not contain any PEM data")
@@ -17,12 +18,15 @@ func ParsePublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
if p.Type != pemType {
return nil, fmt.Errorf("pubkey file does not contain a public key")
}
pubKey, err := x509.ParsePKIXPublicKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("error failed to parse public key: %w", err)
}
return x509.ParsePKIXPublicKey(p.Bytes)
}
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
func ParseECDSAPublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
pk, err := ParsePublicKey(pubkeyBytes)
if err != nil {
return nil, err
}
ecdsaPubKey, ok := pk.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("error public key is not an ecdsa key: %w", err)
}
@@ -34,6 +38,5 @@ func ConvertToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("error failed to marshal public key: %w", err)
}
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: pubKeyBytes}), nil
}