Skip to content

Commit

Permalink
doc: add signature documents
Browse files Browse the repository at this point in the history
Signed-off-by: Binbin Li <[email protected]>
  • Loading branch information
binbin-li committed Aug 19, 2022
1 parent dee4d27 commit c05f579
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 78 deletions.
39 changes: 20 additions & 19 deletions signature/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
"fmt"
)

// Algorithm lists supported algorithms.
// Algorithm defines the signature algorithm.
type Algorithm int

// One of following supported specs
// https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
// Signature algorithms supported by this library.
//
// Reference: https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection
const (
AlgorithmPS256 Algorithm = 1 + iota // RSASSA-PSS with SHA-256
AlgorithmPS384 // RSASSA-PSS with SHA-384
Expand All @@ -22,20 +23,7 @@ const (
AlgorithmES512 // ECDSA on secp521r1 with SHA-512
)

// Hash returns the corresponding crypto hash algorithm.
func (alg Algorithm) Hash() crypto.Hash {
switch alg {
case AlgorithmPS256, AlgorithmES256:
return crypto.SHA256
case AlgorithmPS384, AlgorithmES384:
return crypto.SHA384
case AlgorithmPS512, AlgorithmES512:
return crypto.SHA512
}
return 0
}

// KeyType defines the key type
// KeyType defines the key type.
type KeyType int

const (
Expand All @@ -49,7 +37,20 @@ type KeySpec struct {
Size int
}

// ExtractKeySpec extracts keySpec from the signing certificate
// Hash returns the hash function of the algorithm.
func (alg Algorithm) Hash() crypto.Hash {
switch alg {
case AlgorithmPS256, AlgorithmES256:
return crypto.SHA256
case AlgorithmPS384, AlgorithmES384:
return crypto.SHA384
case AlgorithmPS512, AlgorithmES512:
return crypto.SHA512
}
return 0
}

// ExtractKeySpec extracts KeySpec from the signing certificate.
func ExtractKeySpec(signingCert *x509.Certificate) (KeySpec, error) {
switch key := signingCert.PublicKey.(type) {
case *rsa.PublicKey:
Expand Down Expand Up @@ -80,7 +81,7 @@ func ExtractKeySpec(signingCert *x509.Certificate) (KeySpec, error) {
return KeySpec{}, fmt.Errorf("invalid public key type")
}

// SignatureAlgorithm returns the signing algorithm associated with KeyType k.
// SignatureAlgorithm returns the signing algorithm associated with the KeySpec.
func (k KeySpec) SignatureAlgorithm() Algorithm {
switch k.Type {
case KeyTypeEC:
Expand Down
20 changes: 12 additions & 8 deletions signature/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package signature

import "fmt"

// Envelope provides functions to basic functions to manipulate signatures
// Envelope provides functions to basic functions to manipulate signatures.
type Envelope interface {
// Sign generates and sign the envelope according to the sign request.
Sign(req *SignRequest) ([]byte, error)
Expand All @@ -20,22 +20,25 @@ type Envelope interface {
SignerInfo() (*SignerInfo, error)
}

// NewEnvelopeFunc defines a function to create a new Envelope
// NewEnvelopeFunc defines a function to create a new Envelope.
type NewEnvelopeFunc func() Envelope

// ParseEnvelopeFunc defines a function to create a new Envelope with given
// envelope bytes
// ParseEnvelopeFunc defines a function to create a new Envelope by given
// envelope bytes.
type ParseEnvelopeFunc func([]byte) (Envelope, error)

// envelopeFunc wraps functions to create new envelopes
type envelopeFunc struct {
newFunc NewEnvelopeFunc
parseFunc ParseEnvelopeFunc
}

// envelopeFuncs maps envelope media type to corresponding constructors
var envelopeFuncs = make(map[string]envelopeFunc)

// RegisterEnvelopeType registers newFunc and parseFunc for the given mediaType
// Thoese functions are intended to be called when creating a new envelope
// RegisterEnvelopeType registers newFunc and parseFunc for the given mediaType.
// Those functions are intended to be called when creating a new envelope.
// It will be called while inializing the built-in envelopes(JWS/COSE).
func RegisterEnvelopeType(mediaType string, newFunc NewEnvelopeFunc, parseFunc ParseEnvelopeFunc) error {
if newFunc == nil || parseFunc == nil {
return fmt.Errorf("required functions not provided")
Expand All @@ -59,7 +62,7 @@ func RegisteredEnvelopeTypes() []string {
return types
}

// NewEnvelope returns an envelope of given media type
// NewEnvelope generates an envelope of given media type.
func NewEnvelope(mediaType string) (Envelope, error) {
envelopeFunc, ok := envelopeFuncs[mediaType]
if !ok {
Expand All @@ -68,7 +71,8 @@ func NewEnvelope(mediaType string) (Envelope, error) {
return envelopeFunc.newFunc(), nil
}

// NewEnvelope returns an envelope generated by given bytes and media type
// NewEnvelope generates an envelope by given envelope bytes with specified
// media type.
func ParseEnvelope(mediaType string, envelopeBytes []byte) (Envelope, error) {
envelopeFunc, ok := envelopeFuncs[mediaType]
if !ok {
Expand Down
17 changes: 12 additions & 5 deletions signature/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type MalformedSignatureError struct {
Msg string
}

// Error returns the error message.
// Error returns the error message or the default message if not provided.
func (e *MalformedSignatureError) Error() string {
if e.Msg != "" {
return e.Msg
Expand All @@ -22,7 +22,7 @@ type UnsupportedSigningKeyError struct {
Msg string
}

// Error returns the error message.
// Error returns the error message or the default message if not provided.
func (e *UnsupportedSigningKeyError) Error() string {
if e.Msg != "" {
return e.Msg
Expand Down Expand Up @@ -54,6 +54,7 @@ type MalformedSignRequestError struct {
Msg string
}

// Error returns the error message or the default message if not provided.
func (e *MalformedSignRequestError) Error() string {
if e.Msg != "" {
return e.Msg
Expand All @@ -66,26 +67,31 @@ type SignatureAlgoNotSupportedError struct {
Alg string
}

// Error returns the formatted error message.
func (e *SignatureAlgoNotSupportedError) Error() string {
return fmt.Sprintf("signature algorithm %q is not supported", e.Alg)
}

// SignatureIntegrityError is used when the Signature associated is no longer valid.
// SignatureIntegrityError is used when the signature associated is no longer
// valid.
type SignatureIntegrityError struct {
Err error
}

// Error returns the formatted error message.
func (e *SignatureIntegrityError) Error() string {
return fmt.Sprintf("signature is invalid. Error: %s", e.Err.Error())
}

// Unwrap unwraps the internal error.
func (e *SignatureIntegrityError) Unwrap() error {
return e.Err
}

// SignatureNotFoundError is used when signature envelope is not present.
type SignatureNotFoundError struct{}

// Error returns the default error message.
func (e *SignatureNotFoundError) Error() string {
return "signature envelope is not present"
}
Expand All @@ -94,6 +100,7 @@ func (e *SignatureNotFoundError) Error() string {
// trusted certificates.
type SignatureAuthenticityError struct{}

// Error returns the default error message.
func (e *SignatureAuthenticityError) Error() string {
return "signature is not produced by a trusted signer"
}
Expand All @@ -113,7 +120,7 @@ type EnvelopeKeyRepeatedError struct {
Key string
}

// Error returns the formatted error message
// Error returns the formatted error message.
func (e *EnvelopeKeyRepeatedError) Error() string {
return fmt.Sprintf("repeated key: `%s` exists in the envelope.", e.Key)
return fmt.Sprintf("repeated key: %q exists in the envelope.", e.Key)
}
23 changes: 16 additions & 7 deletions signature/internal/base/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ type Envelope struct {
Raw []byte // raw signature
}

// Sign generates signature using given SignRequest.
// Sign generates signature in terms of given SignRequest.
//
// Reference: https://github.com/notaryproject/notaryproject/blob/main/signing-and-verification-workflow.md#signing-steps
func (e *Envelope) Sign(req *signature.SignRequest) ([]byte, error) {
// Sanitize request
req.SigningTime = req.SigningTime.Truncate(time.Second)
Expand All @@ -33,10 +35,14 @@ func (e *Envelope) Sign(req *signature.SignRequest) ([]byte, error) {
return e.Raw, nil
}

// Verify performs integrity and other signature specification related validations.
// Returns the payload to be signed and SignerInfo object containing the information
// about the signature.
// Verify performs integrity and other signature specification related
// validations.
// It returns the payload to be signed and SignerInfo object containing the
// information about the signature.
//
// Reference: https://github.com/notaryproject/notaryproject/blob/main/trust-store-trust-policy-specification.md#steps
func (e *Envelope) Verify() (*signature.Payload, *signature.SignerInfo, error) {
// validation before the core verify process
if len(e.Raw) == 0 {
return nil, nil, &signature.MalformedSignatureError{}
}
Expand All @@ -45,11 +51,13 @@ func (e *Envelope) Verify() (*signature.Payload, *signature.SignerInfo, error) {
return nil, nil, err
}

// core verify process
payload, signerInfo, err := e.Envelope.Verify()
if err != nil {
return nil, nil, err
}

// validation after the core verify process
if err = validatePayload(payload); err != nil {
return nil, nil, err
}
Expand All @@ -61,7 +69,7 @@ func (e *Envelope) Verify() (*signature.Payload, *signature.SignerInfo, error) {
return payload, signerInfo, nil
}

// Payload returns the payload to be signed.
// Payload returns the validated payload to be signed.
func (e *Envelope) Payload() (*signature.Payload, error) {
if len(e.Raw) == 0 {
return nil, &signature.MalformedSignatureError{Msg: "raw signature is empty"}
Expand All @@ -77,7 +85,7 @@ func (e *Envelope) Payload() (*signature.Payload, error) {
return payload, nil
}

// SignerInfo returns information about the Signature envelope.
// SignerInfo returns validated information about the signature envelope.
func (e *Envelope) SignerInfo() (*signature.SignerInfo, error) {
if len(e.Raw) == 0 {
return nil, &signature.MalformedSignatureError{Msg: "raw signature is empty"}
Expand Down Expand Up @@ -211,7 +219,8 @@ func validateCertificateChain(certChain []*x509.Certificate, signTime time.Time,
return nil
}

// getSignatureAlgorithm picks up a recommended signing algorithm for given certificate.
// getSignatureAlgorithm picks up a recommended signing algorithm for given
// certificate.
func getSignatureAlgorithm(signingCert *x509.Certificate) (signature.Algorithm, error) {
keySpec, err := signature.ExtractKeySpec(signingCert)
if err != nil {
Expand Down
33 changes: 20 additions & 13 deletions signature/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,34 @@ import (
"fmt"
)

// Signer is used to sign bytes generated after creating signature envelope.
// Signer is used to sign bytes generated after signature envelope created.
type Signer interface {
// Sign signs the digest and returns the raw signature
// Sign signs the digest and returns the raw signature.
Sign(digest []byte) ([]byte, error)
// CertificateChain returns the certificate chain
CertificateChain() ([]*x509.Certificate, error) // note: check signature first
// KeySpec returns the key specification

// CertificateChain returns the certificate chain.
CertificateChain() ([]*x509.Certificate, error)

// KeySpec returns the key specification.
KeySpec() (KeySpec, error)
}

// LocalSigner is used by built-in signers to sign only
// LocalSigner is used by built-in signers to sign only.
type LocalSigner interface {
Signer
// PrivateKey returns the private key

// PrivateKey returns the private key.
PrivateKey() crypto.PrivateKey
}

// signer is a LocalSigner implementation.
type signer struct {
keySpec KeySpec
key crypto.PrivateKey
certs []*x509.Certificate
}

// NewLocalSigner returns a new signer with certificates and private key
// NewLocalSigner returns a new signer with given certificates and private key.
func NewLocalSigner(certs []*x509.Certificate, key crypto.PrivateKey) (LocalSigner, error) {
if len(certs) == 0 {
return nil, &MalformedArgumentError{
Expand Down Expand Up @@ -80,29 +84,32 @@ func isKeyPair(priv crypto.PrivateKey, pub crypto.PublicKey, keySpec KeySpec) bo
}
}

// Sign signs the digest and returns the raw signature
// Sign signs the digest and returns the raw signature.
// This implementation should never be used by built-in signers.
func (s *signer) Sign(digest []byte) ([]byte, error) {
return nil, fmt.Errorf("local signer doesn't support Sign with digest")
return nil, fmt.Errorf("local signer doesn't support sign with digest")
}

// CertificateChain returns the certificate chain
// CertificateChain returns the certificate chain.
func (s *signer) CertificateChain() ([]*x509.Certificate, error) {
return s.certs, nil
}

// KeySpec returns the key specification
// KeySpec returns the key specification.
func (s *signer) KeySpec() (KeySpec, error) {
return s.keySpec, nil
}

// PrivateKey returns the private key
// PrivateKey returns the private key.
func (s *signer) PrivateKey() crypto.PrivateKey {
return s.key
}

// VerifyAuthenticity verifies the certificate chain in the given SignerInfo
// with one of the trusted certificates and returns a certificate that matches
// with one of the certificates in the SignerInfo.
//
// Reference: https://github.com/notaryproject/notaryproject/blob/main/trust-store-trust-policy-specification.md#steps
func VerifyAuthenticity(signerInfo *SignerInfo, trustedCerts []*x509.Certificate) (*x509.Certificate, error) {
if len(trustedCerts) == 0 {
return nil, &MalformedArgumentError{Param: "trustedCerts"}
Expand Down
Loading

0 comments on commit c05f579

Please sign in to comment.