diff --git a/ecdsa.go b/ecdsa.go index 7e426be..36839ba 100644 --- a/ecdsa.go +++ b/ecdsa.go @@ -57,6 +57,13 @@ func (es *ecdsaKeySigner) Sign(rand io.Reader, content []byte) ([]byte, error) { if err != nil { return nil, err } + return es.SignDigest(rand, digest) +} + +// SignDigest signs message digest with the private key, possibly using +// entropy from rand. +// The resulting signature should follow RFC 8152 section 8. +func (es *ecdsaKeySigner) SignDigest(rand io.Reader, digest []byte) ([]byte, error) { r, s, err := ecdsa.Sign(rand, es.key, digest) if err != nil { return nil, err @@ -86,6 +93,13 @@ func (es *ecdsaCryptoSigner) Sign(rand io.Reader, content []byte) ([]byte, error if err != nil { return nil, err } + return es.SignDigest(rand, digest) +} + +// SignDigest signs message digest with the private key, possibly using +// entropy from rand. +// The resulting signature should follow RFC 8152 section 8. +func (es *ecdsaCryptoSigner) SignDigest(rand io.Reader, digest []byte) ([]byte, error) { sigASN1, err := es.signer.Sign(rand, digest, nil) if err != nil { return nil, err @@ -153,7 +167,15 @@ func (ev *ecdsaVerifier) Verify(content []byte, signature []byte) error { if err != nil { return err } + return ev.VerifyDigest(digest, signature) +} +// VerifyDigest verifies message digest with the public key, returning nil +// for success. +// Otherwise, it returns ErrVerification. +// +// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1 +func (ev *ecdsaVerifier) VerifyDigest(digest, signature []byte) error { // verify signature r, s, err := decodeECDSASignature(ev.key.Curve, signature) if err != nil { diff --git a/rsa.go b/rsa.go index b63098c..9dc045d 100644 --- a/rsa.go +++ b/rsa.go @@ -24,14 +24,20 @@ func (rs *rsaSigner) Algorithm() Algorithm { // // Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8 func (rs *rsaSigner) Sign(rand io.Reader, content []byte) ([]byte, error) { - hash := rs.alg.hashFunc() - digest, err := computeHash(hash, content) + digest, err := rs.alg.computeHash(content) if err != nil { return nil, err } + return rs.SignDigest(rand, digest) +} + +// SignDigest signs message digest with the private key, possibly using +// entropy from rand. +// The resulting signature should follow RFC 8152 section 8. +func (rs *rsaSigner) SignDigest(rand io.Reader, digest []byte) ([]byte, error) { return rs.key.Sign(rand, digest, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, // defined in RFC 8230 sec 2 - Hash: hash, + Hash: rs.alg.hashFunc(), }) } @@ -54,12 +60,20 @@ func (rv *rsaVerifier) Algorithm() Algorithm { // // Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8 func (rv *rsaVerifier) Verify(content []byte, signature []byte) error { - hash := rv.alg.hashFunc() - digest, err := computeHash(hash, content) + digest, err := rv.alg.computeHash(content) if err != nil { return err } - if err := rsa.VerifyPSS(rv.key, hash, digest, signature, &rsa.PSSOptions{ + return rv.VerifyDigest(digest, signature) +} + +// VerifyDigest verifies message digest with the public key, returning nil +// for success. +// Otherwise, it returns ErrVerification. +// +// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1 +func (rv *rsaVerifier) VerifyDigest(digest, signature []byte) error { + if err := rsa.VerifyPSS(rv.key, rv.alg.hashFunc(), digest, signature, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, // defined in RFC 8230 sec 2 }); err != nil { return ErrVerification diff --git a/signer.go b/signer.go index bac4224..dd69d0c 100644 --- a/signer.go +++ b/signer.go @@ -23,6 +23,16 @@ type Signer interface { Sign(rand io.Reader, content []byte) ([]byte, error) } +// DigestSigner is an interface for private keys to sign digested COSE signatures. +type DigestSigner interface { + Signer + + // SignDigest signs message digest with the private key, possibly using + // entropy from rand. + // The resulting signature should follow RFC 8152 section 8. + SignDigest(rand io.Reader, digest []byte) ([]byte, error) +} + // NewSigner returns a signer with a given signing key. // The signing key can be a golang built-in crypto private key, a key in HSM, or // a remote KMS. @@ -34,6 +44,8 @@ type Signer interface { // public key of type `*rsa.PublicKey`, `*ecdsa.PublicKey`, or // `ed25519.PublicKey` are accepted. // +// The returned signer for rsa and ecdsa keys also implements `cose.DigestSigner`. +// // Note: `*rsa.PrivateKey`, `*ecdsa.PrivateKey`, and `ed25519.PrivateKey` // implement `crypto.Signer`. func NewSigner(alg Algorithm, key crypto.Signer) (Signer, error) { diff --git a/verifier.go b/verifier.go index f2a162d..fc65437 100644 --- a/verifier.go +++ b/verifier.go @@ -22,9 +22,21 @@ type Verifier interface { Verify(content, signature []byte) error } +// DigestVerifier is an interface for public keys to verify digested COSE signatures. +type DigestVerifier interface { + Verifier + + // VerifyDigest verifies message digest with the public key, returning nil + // for success. + // Otherwise, it returns ErrVerification. + VerifyDigest(digest, signature []byte) error +} + // NewVerifier returns a verifier with a given public key. // Only golang built-in crypto public keys of type `*rsa.PublicKey`, // `*ecdsa.PublicKey`, and `ed25519.PublicKey` are accepted. +// +// The returned signer for rsa and ecdsa keys also implements `cose.DigestSigner`. func NewVerifier(alg Algorithm, key crypto.PublicKey) (Verifier, error) { switch alg { case AlgorithmPS256, AlgorithmPS384, AlgorithmPS512: