Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Defining an interface for allowing signing and verifying with different envelope types #109

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions cryptoutil/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"io"
"crypto/x509"
)

type ErrVerifyFailed struct{}
Expand All @@ -40,8 +41,16 @@ func (s *ECDSASigner) KeyID() (string, error) {
return GeneratePublicKeyID(&s.priv.PublicKey, s.hash)
}

func (s *ECDSASigner) Sign(r io.Reader) ([]byte, error) {
digest, err := Digest(r, s.hash)
func (s *ECDSASigner) Algorithm() (x509.PublicKeyAlgorithm, crypto.Hash) {
return x509.ECDSA, s.hash
}

func (s *ECDSASigner) Signer() (crypto.Signer, error) {
return s.priv, nil
}

func (s *ECDSASigner) Sign(ctx context.Context, data []byte) ([]byte, error) {
Copy link
Member

@colek42 colek42 Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be deprecated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume you mean the switch from taking in an io.Reader vs Context and []byte?

So this hasn't been documented at all yet, and probably needs doing so, but switching to this is to align closer to the secure systems lab lib, so moving over to it is less hassle when we do.

However, passing in an io.Reader might be more beneficial for making all of this test driven, so it probably warrants a wider discussion. Also there might be other perfectly good reasons for this being passed as an argument.

To answer the original question, I am not sure about the need for deprecation inside this cryptoutil library more generally. Are we intending for it to be used directly? I have also been thinking about embedding it within the signature package, and only exposing it in the form of NewSignerFromReader/NewSignerFromFile. Not sure what your thoughts are on this?

digest, err := DigestBytes(data, s.hash)
if err != nil {
return nil, err
}
Expand All @@ -66,8 +75,8 @@ func (v *ECDSAVerifier) KeyID() (string, error) {
return GeneratePublicKeyID(v.pub, v.hash)
}

func (v *ECDSAVerifier) Verify(data io.Reader, sig []byte) error {
digest, err := Digest(data, v.hash)
func (v *ECDSAVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
digest, err := DigestBytes(data, v.hash)
if err != nil {
return err
}
Expand All @@ -80,6 +89,10 @@ func (v *ECDSAVerifier) Verify(data io.Reader, sig []byte) error {
return nil
}

func (v *ECDSAVerifier) Public() crypto.PublicKey {
return v.pub
}

func (v *ECDSAVerifier) Bytes() ([]byte, error) {
return PublicPemBytes(v.pub)
}
31 changes: 17 additions & 14 deletions cryptoutil/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/ed25519"
"crypto/x509"
"fmt"
"io"
)

type ED25519Signer struct {
Expand All @@ -33,13 +34,8 @@ func (s *ED25519Signer) KeyID() (string, error) {
return GeneratePublicKeyID(s.priv.Public(), crypto.SHA256)
}

func (s *ED25519Signer) Sign(r io.Reader) ([]byte, error) {
msg, err := io.ReadAll(r)
if err != nil {
return nil, err
}

return ed25519.Sign(s.priv, msg), nil
func (s *ED25519Signer) Sign(ctx context.Context, data []byte) ([]byte, error) {
return ed25519.Sign(s.priv, data), nil
}

func (s *ED25519Signer) Verifier() (Verifier, error) {
Expand All @@ -64,20 +60,27 @@ func (v *ED25519Verifier) KeyID() (string, error) {
return GeneratePublicKeyID(v.pub, crypto.SHA256)
}

func (v *ED25519Verifier) Verify(r io.Reader, sig []byte) error {
msg, err := io.ReadAll(r)
if err != nil {
return err
}
func (s *ED25519Signer) Algorithm() (x509.PublicKeyAlgorithm, crypto.Hash) {
return x509.Ed25519, 0
}

verified := ed25519.Verify(v.pub, msg, sig)
func (s *ED25519Signer) Signer() (crypto.Signer, error) {
return s.priv, nil
}

func (v *ED25519Verifier) Verify(ctx context.Context, payload []byte, sig []byte) error {
verified := ed25519.Verify(v.pub, payload, sig)
if !verified {
return ErrVerifyFailed{}
}

return nil
}

func (v *ED25519Verifier) Public() crypto.PublicKey {
return v.pub
}

func (v *ED25519Verifier) Bytes() ([]byte, error) {
return PublicPemBytes(v.pub)
}
23 changes: 18 additions & 5 deletions cryptoutil/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"io"
"crypto/x509"
)

type RSASigner struct {
Expand All @@ -34,8 +35,16 @@ func (s *RSASigner) KeyID() (string, error) {
return GeneratePublicKeyID(&s.priv.PublicKey, s.hash)
}

func (s *RSASigner) Sign(r io.Reader) ([]byte, error) {
digest, err := Digest(r, s.hash)
func (s *RSASigner) Algorithm() (x509.PublicKeyAlgorithm, crypto.Hash) {
return x509.RSA, s.hash
}

func (s *RSASigner) Signer() (crypto.Signer, error) {
return s.priv, nil
}

func (s *RSASigner) Sign(ctx context.Context, data []byte) ([]byte, error) {
digest, err := DigestBytes(data, s.hash)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -65,8 +74,8 @@ func (v *RSAVerifier) KeyID() (string, error) {
return GeneratePublicKeyID(v.pub, v.hash)
}

func (v *RSAVerifier) Verify(data io.Reader, sig []byte) error {
digest, err := Digest(data, v.hash)
func (v *RSAVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
Copy link
Member

@colek42 colek42 Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how will using []byte vs io.Reader effect memory usage? Verify is likely to be used by a service

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question - one that I haven't thought of. The main motivation was to streamline it with the secure systems lab dsse implementation.

digest, err := DigestBytes(data, v.hash)
if err != nil {
return err
}
Expand All @@ -79,6 +88,10 @@ func (v *RSAVerifier) Verify(data io.Reader, sig []byte) error {
return rsa.VerifyPSS(v.pub, v.hash, digest, sig, pssOpts)
}

func (v *RSAVerifier) Public() crypto.PublicKey {
return v.pub
}

func (v *RSAVerifier) Bytes() ([]byte, error) {
return PublicPemBytes(v.pub)
}
15 changes: 10 additions & 5 deletions cryptoutil/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
Expand All @@ -32,16 +33,20 @@ func (e ErrUnsupportedKeyType) Error() string {
return fmt.Sprintf("unsupported signer key type: %v", e.t)
}

type Signer interface {
KeyIdentifier
Sign(r io.Reader) ([]byte, error)
Verifier() (Verifier, error)
type SignerVerifier struct {
Signer
Verifier
}

type KeyIdentifier interface {
type Signer interface {
KeyID() (string, error)
Sign(ctx context.Context, data []byte) ([]byte, error)
Algorithm() (x509.PublicKeyAlgorithm, crypto.Hash)
Signer() (crypto.Signer, error)
}

type KeyIdentifier interface{}

type TrustBundler interface {
Certificate() *x509.Certificate
Intermediates() []*x509.Certificate
Expand Down
3 changes: 0 additions & 3 deletions cryptoutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ func PublicPemBytes(pub interface{}) ([]byte, error) {
}

pemBytes := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: keyBytes})
if err != nil {
return nil, err
}

return pemBytes, err
}
Expand Down
7 changes: 4 additions & 3 deletions cryptoutil/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
Expand All @@ -26,9 +27,9 @@ import (
)

type Verifier interface {
KeyIdentifier
Verify(body io.Reader, sig []byte) error
Bytes() ([]byte, error)
Verify(ctx context.Context, data, sig []byte) error
KeyID() (string, error)
Public() crypto.PublicKey
}

type VerifierOption func(*verifierOptions)
Expand Down
32 changes: 26 additions & 6 deletions cryptoutil/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/x509"
"encoding/pem"
"io"
"time"
)

Expand Down Expand Up @@ -48,7 +49,7 @@ func (v *X509Verifier) KeyID() (string, error) {
return v.verifier.KeyID()
}

func (v *X509Verifier) Verify(body io.Reader, sig []byte) error {
func (v *X509Verifier) Verify(ctx context.Context, body []byte, sig []byte) error {
rootPool := certificatesToPool(v.roots)
intermediatePool := certificatesToPool(v.intermediates)
if _, err := v.cert.Verify(x509.VerifyOptions{
Expand All @@ -60,7 +61,12 @@ func (v *X509Verifier) Verify(body io.Reader, sig []byte) error {
return err
}

return v.verifier.Verify(body, sig)
return v.verifier.Verify(context.TODO(), body, sig)
}

// TODO: THIS NEEDS TESTED
func (v *X509Verifier) Public() crypto.PublicKey {
return (crypto.PublicKey)(v.cert.PublicKey)
}

func (v *X509Verifier) BelongsToRoot(root *x509.Certificate) error {
Expand Down Expand Up @@ -133,12 +139,26 @@ func (s *X509Signer) KeyID() (string, error) {
return s.signer.KeyID()
}

func (s *X509Signer) Sign(r io.Reader) ([]byte, error) {
return s.signer.Sign(r)
func (s *X509Signer) Algorithm() (x509.PublicKeyAlgorithm, crypto.Hash) {
return s.signer.Algorithm()
}

func (s *X509Signer) Signer() (crypto.Signer, error) {
return s.signer.Signer()
}

func (s *X509Signer) Sign(ctx context.Context, data []byte) ([]byte, error) {
return s.signer.Sign(ctx, data)
}

// TODO: THIS NEEDS TESTED
func (s *X509Signer) Public() crypto.PublicKey {
return (crypto.PublicKey)(s.cert.PublicKey)
}

func (s *X509Signer) Verifier() (Verifier, error) {
verifier, err := s.signer.Verifier()
// Left trustedTime as time.Time{} for now, this may need to be changed
verifier, err := NewX509Verifier(s.cert, s.intermediates, s.roots, time.Time{})
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion dsse/dsse.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (e ErrInvalidThreshold) Error() string {
const PemTypeCertificate = "CERTIFICATE"

type Envelope struct {
Payload []byte `json:"payload"`
Payload string `json:"payload"`
PayloadType string `json:"payloadType"`
Signatures []Signature `json:"signatures"`
}
Expand Down
14 changes: 5 additions & 9 deletions dsse/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package dsse
import (
"bytes"
"context"
"encoding/base64"
"encoding/pem"
"fmt"
"io"
Expand Down Expand Up @@ -47,7 +48,7 @@ func SignWithTimestampers(timestampers ...Timestamper) SignOption {
}
}

func Sign(bodyType string, body io.Reader, opts ...SignOption) (Envelope, error) {
func Sign(bodyType string, body []byte, opts ...SignOption) (Envelope, error) {
so := &signOptions{}
env := Envelope{}
for _, opt := range opts {
Expand All @@ -58,17 +59,12 @@ func Sign(bodyType string, body io.Reader, opts ...SignOption) (Envelope, error)
return env, fmt.Errorf("must have at least one signer, have %v", len(so.signers))
}

bodyBytes, err := io.ReadAll(body)
if err != nil {
return env, err
}

env.PayloadType = bodyType
env.Payload = bodyBytes
env.Payload = base64.StdEncoding.EncodeToString(body)
env.Signatures = make([]Signature, 0)
pae := preauthEncode(bodyType, bodyBytes)
pae := preauthEncode(bodyType, body)
for _, signer := range so.signers {
sig, err := signer.Sign(bytes.NewReader(pae))
sig, err := signer.Sign(context.TODO(), pae)
if err != nil {
return env, err
}
Expand Down
6 changes: 3 additions & 3 deletions dsse/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (e Envelope) Verify(opts ...VerificationOption) ([]PassedVerifier, error) {
return nil, ErrInvalidThreshold(options.threshold)
}

pae := preauthEncode(e.PayloadType, e.Payload)
pae := preauthEncode(e.PayloadType, []byte(e.Payload))
if len(e.Signatures) == 0 {
return nil, ErrNoSignatures{}
}
Expand Down Expand Up @@ -146,7 +146,7 @@ func (e Envelope) Verify(opts ...VerificationOption) ([]PassedVerifier, error) {

for _, verifier := range options.verifiers {
if verifier != nil {
if err := verifier.Verify(bytes.NewReader(pae), sig.Signature); err == nil {
if err := verifier.Verify(context.TODO(), pae, sig.Signature); err == nil {
passedVerifiers = append(passedVerifiers, PassedVerifier{Verifier: verifier})
matchingSigFound = true
}
Expand All @@ -171,7 +171,7 @@ func verifyX509Time(cert *x509.Certificate, sigIntermediates, roots []*x509.Cert
return nil, err
}

if err := verifier.Verify(bytes.NewReader(pae), sig); err != nil {
if err := verifier.Verify(context.TODO(), pae, sig); err != nil {
return nil, err
}

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
github.com/cloudflare/circl v1.3.3 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand All @@ -44,11 +45,14 @@ require (
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/veraison/go-cose v1.1.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
Expand Down
Loading