From 61c0fe4db405ad369660aa6bd83d5d76c7ee4df9 Mon Sep 17 00:00:00 2001 From: Binbin Li Date: Fri, 19 Aug 2022 12:08:41 +0800 Subject: [PATCH] doc: add signature documents (#39) * doc: add signature documents Signed-off-by: Binbin Li * doc: refactor comments Signed-off-by: Binbin Li Signed-off-by: Binbin Li Co-authored-by: Binbin Li --- signature/algorithm.go | 39 ++++++----- signature/envelope.go | 21 +++--- signature/errors.go | 17 +++-- signature/internal/base/envelope.go | 23 ++++-- signature/signer.go | 33 +++++---- signature/types.go | 105 +++++++++++++++++++++------- 6 files changed, 160 insertions(+), 78 deletions(-) diff --git a/signature/algorithm.go b/signature/algorithm.go index 0227ec44..476de3d1 100644 --- a/signature/algorithm.go +++ b/signature/algorithm.go @@ -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 @@ -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 ( @@ -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: @@ -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: diff --git a/signature/envelope.go b/signature/envelope.go index f3e3503c..daab5c53 100644 --- a/signature/envelope.go +++ b/signature/envelope.go @@ -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) @@ -20,22 +20,26 @@ 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 that takes envelope bytes to create +// an Envelope. type ParseEnvelopeFunc func([]byte) (Envelope, error) +// envelopeFunc wraps functions to create and parsenew envelopes. type envelopeFunc struct { newFunc NewEnvelopeFunc parseFunc ParseEnvelopeFunc } +// envelopeFuncs maps envelope media type to corresponding constructors and +// parsers. 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") @@ -59,7 +63,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 { @@ -68,7 +72,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 { diff --git a/signature/errors.go b/signature/errors.go index 7c3f4f37..2428c754 100644 --- a/signature/errors.go +++ b/signature/errors.go @@ -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 @@ -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 @@ -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 @@ -66,19 +67,23 @@ 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 } @@ -86,6 +91,7 @@ func (e *SignatureIntegrityError) Unwrap() error { // 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" } @@ -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" } @@ -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) } diff --git a/signature/internal/base/envelope.go b/signature/internal/base/envelope.go index 95d26123..e39165c7 100644 --- a/signature/internal/base/envelope.go +++ b/signature/internal/base/envelope.go @@ -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) @@ -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{} } @@ -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 } @@ -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"} @@ -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"} @@ -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 { diff --git a/signature/signer.go b/signature/signer.go index 0a2918aa..b4dad866 100644 --- a/signature/signer.go +++ b/signature/signer.go @@ -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{ @@ -80,22 +84,23 @@ 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 } @@ -103,6 +108,8 @@ func (s *signer) PrivateKey() crypto.PrivateKey { // 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"} diff --git a/signature/types.go b/signature/types.go index ef866f6a..cf90c056 100644 --- a/signature/types.go +++ b/signature/types.go @@ -5,64 +5,117 @@ import ( "time" ) +// MediaTypePayloadV1 is the supported content type for signature's payload. +const MediaTypePayloadV1 = "application/vnd.cncf.notary.payload.v1+json" + +// SigningScheme formalizes the feature set (guarantees) provided by +// the signature. +// Reference: https://github.com/notaryproject/notaryproject/blob/main/signing-scheme.md +type SigningScheme string + +// SigningSchemes supported by notation. const ( - // MediaTypePayloadV1 is the supported content type for signature's payload - MediaTypePayloadV1 = "application/vnd.cncf.notary.payload.v1+json" + // notary.x509 sigining scheme. + SigningSchemeX509 SigningScheme = "notary.x509" + + // notary.x509.signingAuthority schema. + SigningSchemeX509SigningAuthority SigningScheme = "notary.x509.signingAuthority" ) -// SignedAttributes represents signed metadata in the Signature envelope +// SignedAttributes represents signed metadata in the signature envelope. +// Reference: https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#signed-attributes type SignedAttributes struct { - SigningTime time.Time - Expiry time.Time + // SigningTime indicates the time at which the signature was generated. + SigningTime time.Time + + // Expiry provides a “best by use” time for the artifact. + Expiry time.Time + + // additional signed attributes in the signature envelope. ExtendedAttributes []Attribute } -// UnsignedAttributes represents unsigned metadata in the Signature envelope +// UnsignedAttributes represents unsigned metadata in the Signature envelope. +// Reference: https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#unsigned-attributes type UnsignedAttributes struct { + // SigningAgent provides the identifier of the software (e.g. Notation) that + // produces the signature on behalf of the user. SigningAgent string } -// Attribute represents metadata in the Signature envelope +// Attribute represents metadata in the Signature envelope. type Attribute struct { - Key string + // Key is the key name of the attribute. + Key string + + // Critical marks the attribute that MUST be processed by a verifier. Critical bool - Value interface{} + + // Value is the value of the attribute. + Value interface{} } // SignRequest is used to generate Signature. type SignRequest struct { - Payload Payload - Signer Signer - SigningTime time.Time - Expiry time.Time + // Payload is the payload to be signed. + Payload Payload + + // Signer is the signer used to sign the digest. + Signer Signer + + // SigningTime is the time at which the signature was generated. + SigningTime time.Time + + // Expiry provides a “best by use” time for the artifact. + Expiry time.Time + + // ExtendedSignedAttributes is additional signed attributes in the + // signature envelope. ExtendedSignedAttributes []Attribute + + // SigningAgent provides the identifier of the software (e.g. Notation) + // that produced the signature on behalf of the user. SigningAgent string + + // SigningScheme defines the Notary v2 Signing Scheme used by the signature. SigningScheme SigningScheme } -// SignerInfo represents a parsed signature envelope that is agnostic to signature -// envelope format. +// SignerInfo represents a parsed signature envelope that is agnostic to +// signature envelope format. type SignerInfo struct { + // SignedAttributes are additional metadata required to support the + // signature verification process. SignedAttributes SignedAttributes + + // UnsignedAttributes are considered unsigned with respect to the signing + // key that generates the signature. UnsignedAttributes UnsignedAttributes + + // SignatureAlgorithm defines the signature algorithm. SignatureAlgorithm Algorithm + + // CertificateChain is an ordered list of X.509 public certificates + // associated with the signing key used to generate the signature. + // The ordered list starts with the signing certificates, any intermediate + // certificates and ends with the root certificate. CertificateChain []*x509.Certificate + + // Signature is the bytes generated from the signature. Signature []byte + + // TimestampSignature is a counter signature providing authentic timestamp. TimestampSignature []byte + + // SigningScheme defines the Notary v2 Signing Scheme used by the signature. SigningScheme SigningScheme } -// Payload represents payload in bytes and its content type +// Payload represents payload in bytes and its content type. type Payload struct { + // ContentType specifies the content type of payload. ContentType string - Content []byte -} -// SigningScheme formalizes the feature set (guarantees) provided by -// the signature. -type SigningScheme string - -const ( - SigningSchemeX509 SigningScheme = "notary.x509" - SigningSchemeX509SigningAuthority SigningScheme = "notary.x509.signingAuthority" -) + // Content contains the raw bytes of the payload. + Content []byte +}