Wrap the cause of signature verification errors.

Instead of swallowing the cause of verification errors,
we use error wrapping to communicate the cause to the caller.
This commit is contained in:
M. Thiercelin 2023-03-06 13:52:17 +01:00
parent 58dbea76e7
commit e9fca4d62f
No known key found for this signature in database
GPG key ID: 29581E7E24EBEC0A
5 changed files with 48 additions and 25 deletions

View file

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- The `SignatureVerificationError` struct now has a `Cause error` field, which is returned by the the Unwrap function. The cause is also included in the error message.
NB: If the caller was relying on the exact message of the error, it might break the flow.
## [2.6.1] 2023-03-22 ## [2.6.1] 2023-03-22
### Security fix ### Security fix

View file

@ -303,7 +303,7 @@ func TestMessageVerificationNotSignedNoVerifier(t *testing.T) {
func TestMessageVerificationNotSignedFailed(t *testing.T) { func TestMessageVerificationNotSignedFailed(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_13.asc") callbackResults := runScenario(t, "testdata/mime/scenario_13.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNotSigned(), newSignatureFailed()} var expectedErrors = []SignatureVerificationError{newSignatureNotSigned(), newSignatureFailed(nil)}
compareErrors(expectedErrors, callbackResults.onError, t) compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{3} expectedStatus := []int{3}
compareStatus(expectedStatus, callbackResults.onVerified, t) compareStatus(expectedStatus, callbackResults.onVerified, t)
@ -335,7 +335,7 @@ func TestMessageVerificationNoVerifierNoVerifier(t *testing.T) {
func TestMessageVerificationNoVerifierFailed(t *testing.T) { func TestMessageVerificationNoVerifierFailed(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_23.asc") callbackResults := runScenario(t, "testdata/mime/scenario_23.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNoVerifier(), newSignatureFailed()} var expectedErrors = []SignatureVerificationError{newSignatureNoVerifier(), newSignatureFailed(nil)}
compareErrors(expectedErrors, callbackResults.onError, t) compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{3} expectedStatus := []int{3}
compareStatus(expectedStatus, callbackResults.onVerified, t) compareStatus(expectedStatus, callbackResults.onVerified, t)

View file

@ -29,23 +29,33 @@ var allowedHashes = []crypto.Hash{
type SignatureVerificationError struct { type SignatureVerificationError struct {
Status int Status int
Message string Message string
Cause error
} }
// Error is the base method for all errors. // Error is the base method for all errors.
func (e SignatureVerificationError) Error() string { func (e SignatureVerificationError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("Signature Verification Error: %v caused by %v", e.Message, e.Cause)
}
return fmt.Sprintf("Signature Verification Error: %v", e.Message) return fmt.Sprintf("Signature Verification Error: %v", e.Message)
} }
// Unwrap returns the cause of failure.
func (e SignatureVerificationError) Unwrap() error {
return e.Cause
}
// ------------------ // ------------------
// Internal functions // Internal functions
// ------------------ // ------------------
// newSignatureFailed creates a new SignatureVerificationError, type // newSignatureFailed creates a new SignatureVerificationError, type
// SignatureFailed. // SignatureFailed.
func newSignatureFailed() SignatureVerificationError { func newSignatureFailed(cause error) SignatureVerificationError {
return SignatureVerificationError{ return SignatureVerificationError{
Status: constants.SIGNATURE_FAILED, Status: constants.SIGNATURE_FAILED,
Message: "Invalid signature", Message: "Invalid signature",
Cause: cause,
} }
} }
@ -108,7 +118,7 @@ func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) er
return newSignatureNoVerifier() return newSignatureNoVerifier()
} }
if md.SignatureError != nil { if md.SignatureError != nil {
return newSignatureFailed() return newSignatureFailed(md.SignatureError)
} }
if md.Signature == nil || if md.Signature == nil ||
md.Signature.Hash < allowedHashes[0] || md.Signature.Hash < allowedHashes[0] ||
@ -238,21 +248,25 @@ func verifySignature(
_, err = signatureReader.Seek(0, io.SeekStart) _, err = signatureReader.Seek(0, io.SeekStart)
if err != nil { if err != nil {
return nil, newSignatureFailed() return nil, newSignatureFailed(err)
} }
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config) sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
} }
} }
if err != nil || sig == nil || signer == nil { if err != nil {
return nil, newSignatureFailed() return nil, newSignatureFailed(err)
}
if sig == nil || signer == nil {
return nil, newSignatureFailed(errors.New("gopenpgp: no signer or valid signature"))
} }
if verificationContext != nil { if verificationContext != nil {
err := verificationContext.verifyContext(sig) err := verificationContext.verifyContext(sig)
if err != nil { if err != nil {
return nil, newSignatureFailed() return nil, newSignatureFailed(err)
} }
} }

View file

@ -110,7 +110,7 @@ func (sc *SignatureCollector) Accept(
case errors.Is(err, pgpErrors.ErrUnknownIssuer): case errors.Is(err, pgpErrors.ErrUnknownIssuer):
sc.verified = newSignatureNoVerifier() sc.verified = newSignatureNoVerifier()
default: default:
sc.verified = newSignatureFailed() sc.verified = newSignatureFailed(err)
} }
} else { } else {
sc.verified = newSignatureNoVerifier() sc.verified = newSignatureNoVerifier()

View file

@ -75,11 +75,25 @@ func TestVerifyTextDetachedSig(t *testing.T) {
} }
} }
func checkVerificationError(t *testing.T, err error, expectedStatus int) { //nolint: unparam
if err == nil {
t.Fatalf("Expected a verification error")
}
castedErr := &SignatureVerificationError{}
isType := errors.As(err, castedErr)
if !isType {
t.Fatalf("Error was not a verification errror: %v", err)
}
if castedErr.Status != expectedStatus {
t.Fatalf("Expected status to be %d got %d", expectedStatus, castedErr.Status)
}
}
func TestVerifyTextDetachedSigWrong(t *testing.T) { func TestVerifyTextDetachedSigWrong(t *testing.T) {
fakeMessage := NewPlainMessageFromString("wrong text") fakeMessage := NewPlainMessageFromString("wrong text")
verificationError := keyRingTestPublic.VerifyDetached(fakeMessage, textSignature, testTime) verificationError := keyRingTestPublic.VerifyDetached(fakeMessage, textSignature, testTime)
assert.EqualError(t, verificationError, "Signature Verification Error: Invalid signature") checkVerificationError(t, verificationError, constants.SIGNATURE_FAILED)
err := &SignatureVerificationError{} err := &SignatureVerificationError{}
_ = errors.As(verificationError, err) _ = errors.As(verificationError, err)
@ -342,9 +356,7 @@ func Test_VerifyDetachedWithUnknownCriticalContext(t *testing.T) {
0, 0,
) )
// then // then
if err == nil || !errors.Is(err, newSignatureFailed()) { checkVerificationError(t, err, constants.SIGNATURE_FAILED)
t.Fatalf("Expected a verification error")
}
} }
func Test_VerifyDetachedWithUnKnownNonCriticalContext(t *testing.T) { func Test_VerifyDetachedWithUnKnownNonCriticalContext(t *testing.T) {
@ -423,9 +435,7 @@ func Test_VerifyDetachedWithWrongContext(t *testing.T) {
verificationContext, verificationContext,
) )
// then // then
if err == nil || !errors.Is(err, newSignatureFailed()) { checkVerificationError(t, err, constants.SIGNATURE_FAILED)
t.Fatalf("Expected a verification error")
}
} }
func Test_VerifyDetachedWithMissingNonRequiredContext(t *testing.T) { func Test_VerifyDetachedWithMissingNonRequiredContext(t *testing.T) {
@ -481,9 +491,7 @@ func Test_VerifyDetachedWithMissingRequiredContext(t *testing.T) {
verificationContext, verificationContext,
) )
// then // then
if err == nil || !errors.Is(err, newSignatureFailed()) { checkVerificationError(t, err, constants.SIGNATURE_FAILED)
t.Fatalf("Expected a verification error")
}
} }
func Test_VerifyDetachedWithMissingRequiredContextBeforeCutoff(t *testing.T) { func Test_VerifyDetachedWithMissingRequiredContextBeforeCutoff(t *testing.T) {
@ -553,9 +561,7 @@ func Test_VerifyDetachedWithMissingRequiredContextAfterCutoff(t *testing.T) {
verificationContext, verificationContext,
) )
// then // then
if err == nil || !errors.Is(err, newSignatureFailed()) { checkVerificationError(t, err, constants.SIGNATURE_FAILED)
t.Fatalf("Expected a verification error")
}
} }
func Test_VerifyDetachedWithDoubleContext(t *testing.T) { func Test_VerifyDetachedWithDoubleContext(t *testing.T) {
@ -581,7 +587,5 @@ func Test_VerifyDetachedWithDoubleContext(t *testing.T) {
verificationContext, verificationContext,
) )
// then // then
if err == nil || !errors.Is(err, newSignatureFailed()) { checkVerificationError(t, err, constants.SIGNATURE_FAILED)
t.Fatalf("Expected a verification error")
}
} }