Instead of swallowing the cause of verification errors, we use error wrapping to communicate the cause to the caller.
342 lines
9.8 KiB
Go
342 lines
9.8 KiB
Go
package crypto
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// Corresponding key in testdata/mime_privateKey.
|
|
var MIMEKeyPassword = []byte("test")
|
|
|
|
type Callbacks struct {
|
|
Testing *testing.T
|
|
}
|
|
|
|
func (t *Callbacks) OnBody(body string, mimetype string) {
|
|
assert.Exactly(t.Testing, readTestFile("mime_decryptedBody", false), body)
|
|
}
|
|
|
|
func (t Callbacks) OnAttachment(headers string, data []byte) {
|
|
assert.Exactly(t.Testing, 1, data)
|
|
}
|
|
|
|
func (t Callbacks) OnEncryptedHeaders(headers string) {
|
|
assert.Exactly(t.Testing, "", headers)
|
|
}
|
|
|
|
func (t Callbacks) OnVerified(verified int) {
|
|
}
|
|
|
|
func (t Callbacks) OnError(err error) {
|
|
t.Testing.Fatal("Error in decrypting MIME message: ", err)
|
|
}
|
|
|
|
func TestDecrypt(t *testing.T) {
|
|
callbacks := Callbacks{
|
|
Testing: t,
|
|
}
|
|
|
|
privateKey, err := NewKeyFromArmored(readTestFile("mime_privateKey", false))
|
|
if err != nil {
|
|
t.Fatal("Cannot unarmor private key:", err)
|
|
}
|
|
|
|
privateKey, err = privateKey.Unlock(MIMEKeyPassword)
|
|
if err != nil {
|
|
t.Fatal("Cannot unlock private key:", err)
|
|
}
|
|
|
|
privateKeyRing, err := NewKeyRing(privateKey)
|
|
if err != nil {
|
|
t.Fatal("Cannot create private keyring:", err)
|
|
}
|
|
|
|
message, err := NewPGPMessageFromArmored(readTestFile("mime_pgpMessage", false))
|
|
if err != nil {
|
|
t.Fatal("Cannot decode armored message:", err)
|
|
}
|
|
|
|
privateKeyRing.DecryptMIMEMessage(
|
|
message,
|
|
nil,
|
|
&callbacks,
|
|
GetUnixTime())
|
|
}
|
|
|
|
func TestParse(t *testing.T) {
|
|
body, atts, attHeaders, err := parseMIME(readTestFile("mime_testMessage", false), nil)
|
|
|
|
if err != nil {
|
|
t.Fatal("Expected no error while parsing message, got:", err)
|
|
}
|
|
|
|
_ = atts
|
|
_ = attHeaders
|
|
|
|
bodyData, _ := body.GetBody()
|
|
assert.Exactly(t, readTestFile("mime_decodedBody", true), bodyData)
|
|
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(nil)}
|
|
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(nil)}
|
|
compareErrors(expectedErrors, callbackResults.onError, t)
|
|
expectedStatus := []int{3}
|
|
compareStatus(expectedStatus, callbackResults.onVerified, t)
|
|
}
|