Fix verification of signature for PGP/MIME messages

We fix the verification of embedded and PGM/MIME signatures
when decrypting PGP/MIME messages.
This commit is contained in:
marin thiercelin 2021-12-15 11:01:04 +01:00
parent b97b3d886b
commit e2f03af655
No known key found for this signature in database
GPG key ID: 117C025B1F21B2C6
17 changed files with 749 additions and 8 deletions

View file

@ -1,6 +1,9 @@
package crypto
import (
"errors"
"io/ioutil"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@ -79,3 +82,261 @@ func TestParse(t *testing.T) {
assert.Exactly(t, readTestFile("mime_decodedBodyHeaders", false), body.GetHeaders())
assert.Exactly(t, 2, len(atts))
}
type testMIMECallbacks struct {
onBody []struct{ body, mimetype string }
onAttachment []struct {
headers string
data []byte
}
onEncryptedHeaders []string
onVerified []int
onError []error
}
func (tc *testMIMECallbacks) OnBody(body string, mimetype string) {
tc.onBody = append(tc.onBody, struct {
body string
mimetype string
}{body, mimetype})
}
func (tc *testMIMECallbacks) OnAttachment(headers string, data []byte) {
tc.onAttachment = append(tc.onAttachment, struct {
headers string
data []byte
}{headers, data})
}
func (tc *testMIMECallbacks) OnEncryptedHeaders(headers string) {
tc.onEncryptedHeaders = append(tc.onEncryptedHeaders, headers)
}
func (tc *testMIMECallbacks) OnVerified(status int) {
tc.onVerified = append(tc.onVerified, status)
}
func (tc *testMIMECallbacks) OnError(err error) {
tc.onError = append(tc.onError, err)
}
func loadPrivateKeyRing(file string, passphrase string) (*KeyRing, error) {
armored, err := ioutil.ReadFile(filepath.Clean(file))
if err != nil {
return nil, err
}
key, err := NewKeyFromArmored(string(armored))
if err != nil {
return nil, err
}
unlockedKey, err := key.Unlock([]byte(passphrase))
if err != nil {
return nil, err
}
keyRing, err := NewKeyRing(unlockedKey)
if err != nil {
return nil, err
}
return keyRing, nil
}
func loadPublicKeyRing(file string) (*KeyRing, error) {
armored, err := ioutil.ReadFile(filepath.Clean(file))
if err != nil {
return nil, err
}
key, err := NewKeyFromArmored(string(armored))
if err != nil {
return nil, err
}
if key.IsPrivate() {
publicKey, err := key.GetPublicKey()
if err != nil {
return nil, err
}
key, err = NewKey(publicKey)
if err != nil {
return nil, err
}
}
keyRing, err := NewKeyRing(key)
if err != nil {
return nil, err
}
return keyRing, nil
}
func loadMessage(file string) (*PGPMessage, error) {
armored, err := ioutil.ReadFile(filepath.Clean(file))
if err != nil {
return nil, err
}
message, err := NewPGPMessageFromArmored(string(armored))
if err != nil {
return nil, err
}
return message, nil
}
func runScenario(t *testing.T, messageFile string) *testMIMECallbacks {
decryptionKeyRing, err := loadPrivateKeyRing("testdata/mime/decryption-key.asc", "test_passphrase")
if err != nil {
t.Errorf("Failed to load decryption key %v", err)
}
verificationKeyRing, err := loadPublicKeyRing("testdata/mime/verification-key.asc")
if err != nil {
t.Errorf("Failed to load verification key %v", err)
}
message, err := loadMessage(messageFile)
if err != nil {
t.Errorf("Failed to load message %v", err)
}
callbacks := &testMIMECallbacks{}
decryptionKeyRing.DecryptMIMEMessage(message, verificationKeyRing, callbacks, 0)
return callbacks
}
func compareStatus(expected []int, actual []int, t *testing.T) {
if len(actual) != len(expected) {
t.Errorf("Expected %v, got %v", expected, actual)
} else {
for i, actualStatus := range actual {
if actualStatus != expected[i] {
t.Errorf("Expected status %v, got %v", expected[i], actualStatus)
}
}
}
}
func TestMessageVerificationOkOk(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_00.asc")
if len(callbackResults.onError) != 0 {
for _, err := range callbackResults.onError {
t.Errorf("Expected no errors got %v", err)
}
}
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationOkNotSigned(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_01.asc")
if len(callbackResults.onError) != 0 {
for _, err := range callbackResults.onError {
t.Errorf("Expected no errors got %v", err)
}
}
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationOkNoVerifier(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_02.asc")
if len(callbackResults.onError) != 0 {
for _, err := range callbackResults.onError {
t.Errorf("Expected no errors got %v", err)
}
}
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationOkFailed(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_03.asc")
if len(callbackResults.onError) != 0 {
for _, err := range callbackResults.onError {
t.Errorf("Expected no errors got %v", err)
}
}
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNotSignedOk(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_10.asc")
if len(callbackResults.onError) != 0 {
for _, err := range callbackResults.onError {
t.Errorf("Expected no errors got %v", err)
}
}
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func checkIsSigErr(t *testing.T, err error) int {
sigErr := &SignatureVerificationError{}
if errors.As(err, &sigErr) {
return sigErr.Status
}
t.Errorf("Expected a signature verification error, got %v", err)
return -1
}
func compareErrors(expected []SignatureVerificationError, actual []error, t *testing.T) {
if len(actual) != len(expected) {
t.Errorf("Expected %v, got %v", expected, actual)
} else {
for i, err := range actual {
actualStatus := checkIsSigErr(t, err)
if actualStatus != expected[i].Status {
t.Errorf("Expected sig error with status %v, got %v", expected[i].Status, actualStatus)
}
}
}
}
func TestMessageVerificationNotSignedNotSigned(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_11.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNotSigned(), newSignatureNotSigned()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{1}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNotSignedNoVerifier(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_12.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNotSigned(), newSignatureNoVerifier()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{2}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNotSignedFailed(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_13.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNotSigned(), newSignatureFailed()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{3}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNoVerifierOk(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_20.asc")
var expectedErrors = []SignatureVerificationError{}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{0}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNoVerifierNotSigned(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_21.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNoVerifier(), newSignatureNotSigned()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{2}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNoVerifierNoVerifier(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_22.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNoVerifier(), newSignatureNoVerifier()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{2}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}
func TestMessageVerificationNoVerifierFailed(t *testing.T) {
callbackResults := runScenario(t, "testdata/mime/scenario_23.asc")
var expectedErrors = []SignatureVerificationError{newSignatureNoVerifier(), newSignatureFailed()}
compareErrors(expectedErrors, callbackResults.onError, t)
expectedStatus := []int{3}
compareStatus(expectedStatus, callbackResults.onVerified, t)
}