From 3b113da5c903f3aefc81d05c6165bcfdd3cfb408 Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Fri, 26 Jan 2018 18:32:33 +0100 Subject: [PATCH] Add unit tests for signature verification --- entities_test.go | 126 ++++++++++++----------------------------- entity_comment_test.go | 23 +++++--- entity_like_test.go | 22 ++++--- helper_test.go | 37 ++++++++++++ salmon_test.go | 50 ++++++++++++++-- signature_test.go | 52 +++++++++++++++++ 6 files changed, 200 insertions(+), 110 deletions(-) create mode 100644 signature_test.go diff --git a/entities_test.go b/entities_test.go index f624ea7..d2f3e85 100644 --- a/entities_test.go +++ b/entities_test.go @@ -19,106 +19,41 @@ package federation import ( "testing" + "reflect" "github.com/Zauberstuhl/go-xml" ) func TestEntitiesUnmarshalXML(t *testing.T) { var entity Entity - - var retractionRaw = []byte(``) - var profileRaw = []byte(``) - var statusMessageRaw = []byte(``) - var reshareRaw = []byte(``) - var commentRaw = []byte(``) - var likeRaw = []byte(``) - var contactRaw = []byte(``) - var notSupportedRaw = []byte(``) - - err := xml.Unmarshal(retractionRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityRetraction); !ok { - t.Errorf("Expected to be 'like', got %v", data) - } - err = xml.Unmarshal(retractionRaw[:len(retractionRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") - } - - err = xml.Unmarshal(profileRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityProfile); !ok { - t.Errorf("Expected to be 'profile', got %v", data) - } - err = xml.Unmarshal(profileRaw[:len(profileRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") + var tests = []struct{ + Type string + Raw []byte + }{ + {Type: "EntityRetraction", Raw: []byte(``)}, + {Type: "EntityProfile", Raw: []byte(``)}, + {Type: "EntityStatusMessage", Raw: []byte(``)}, + {Type: "EntityReshare", Raw: []byte(``)}, + {Type: "EntityComment", Raw: []byte(``)}, + {Type: "EntityLike", Raw: []byte(``)}, + {Type: "EntityContact", Raw: []byte(``)}, } - err = xml.Unmarshal(statusMessageRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityStatusMessage); !ok { - t.Errorf("Expected to be 'status_message', got %v", data) - } - err = xml.Unmarshal(statusMessageRaw[:len(statusMessageRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") + for i, test := range tests { + err := xml.Unmarshal(test.Raw, &entity) + if err != nil { + t.Errorf("#%d: Some error occured while parsing: %v", i, err) + } + name := reflect.TypeOf(entity.Data).Name() + if test.Type != name { + t.Errorf("#%d: Expected to be '%s', got '%s'", i, test.Type, name) + } + err = xml.Unmarshal(test.Raw[:len(test.Raw)-1], &entity) + if err == nil { + t.Errorf("#%d: Expected an error, got nil", i) + } } - err = xml.Unmarshal(reshareRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityReshare); !ok { - t.Errorf("Expected to be 'reshare', got %v", data) - } - err = xml.Unmarshal(reshareRaw[:len(reshareRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") - } - - err = xml.Unmarshal(commentRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityComment); !ok { - t.Errorf("Expected to be 'comment', got %v", data) - } - err = xml.Unmarshal(commentRaw[:len(commentRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") - } - - err = xml.Unmarshal(likeRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityLike); !ok { - t.Errorf("Expected to be 'like', got %v", data) - } - err = xml.Unmarshal(likeRaw[:len(likeRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") - } - - err = xml.Unmarshal(contactRaw, &entity) - if err != nil { - t.Errorf("Some error occured while parsing: %v", err) - } - if data, ok := entity.Data.(EntityContact); !ok { - t.Errorf("Expected to be 'contact', got %v", data) - } - err = xml.Unmarshal(contactRaw[:len(contactRaw)-1], &entity) - if err == nil { - t.Errorf("Expected an error, got nil") - } - - err = xml.Unmarshal(notSupportedRaw, &entity) + err := xml.Unmarshal([]byte(``), &entity) if err == nil { t.Errorf("Expected an error, got nil") } @@ -150,6 +85,15 @@ func TestEntitiesTimeMarshalAndUnmarshal(t *testing.T) { t.Errorf("Expected to be '%s', got '%s'", result, rawXml) } + timeTime, err := origTime.CreatedAt.Time() + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + if timeTime.Format(TIME_FORMAT) != time { + t.Errorf("Expected to be '%s', got '%s'", + time, timeTime.Format(TIME_FORMAT)) + } + // XXX the application server uses time.Now if this happens // we should change that and let the library decide what is best //err = xml.Unmarshal([]byte(""), &origTime) diff --git a/entity_comment_test.go b/entity_comment_test.go index cbf8615..6818699 100644 --- a/entity_comment_test.go +++ b/entity_comment_test.go @@ -22,16 +22,25 @@ import ( "time" ) -func TestCommentAppendSignature(t *testing.T) { - comment := EntityComment{ - Author: "author@localhost", - Guid: "1234", - ParentGuid: "4321", - Text: "hello world", +var comment = EntityComment{ + Author: "author@localhost", + Guid: "1234", + ParentGuid: "4321", + AuthorSignature: "1234", + Text: "hello world", +} + +func TestCommentSignature(t *testing.T) { + if comment.Signature() != comment.AuthorSignature { + t.Errorf("Expected to be '%s', got '%s'", + comment.AuthorSignature, comment.Signature()) } +} + +func TestCommentAppendSignature(t *testing.T) { comment.CreatedAt.New(time.Now()) - if comment.AuthorSignature != "" { + if comment.AuthorSignature != "1234" { t.Errorf("Expected to be empty, got %s", comment.AuthorSignature) } diff --git a/entity_like_test.go b/entity_like_test.go index 0b30038..76805bd 100644 --- a/entity_like_test.go +++ b/entity_like_test.go @@ -19,15 +19,23 @@ package federation import "testing" -func TestLikeAppendSignature(t *testing.T) { - like := EntityLike{ - Positive: true, - Guid: "1234", - ParentGuid: "4321", - ParentType: "Post", - Author: "author@localhost", +var like = EntityLike{ + Positive: true, + Guid: "1234", + ParentGuid: "4321", + ParentType: "Post", + Author: "author@localhost", +} + +func TestLikeSignature(t *testing.T) { + like.AuthorSignature = "1234" + if like.Signature() != like.AuthorSignature { + t.Errorf("Expected to be '%s', got '%s'", + like.AuthorSignature, like.Signature()) } +} +func TestLikeAppendSignature(t *testing.T) { privKey, err := ParseRSAPrivateKey(TEST_PRIV_KEY) if err != nil { t.Errorf("Some error occured while parsing: %v", err) diff --git a/helper_test.go b/helper_test.go index 51478d7..9957c24 100644 --- a/helper_test.go +++ b/helper_test.go @@ -20,6 +20,7 @@ package federation import ( "crypto/rsa" "testing" + "strings" ) var TEST_AUTHOR = `diaspora_2nd@localhost:3001` @@ -149,3 +150,39 @@ func TestParseStringHelper(t *testing.T) { t.Errorf("Expected an error, got nil") } } + +func TestExractSignatureText(t *testing.T) { + var entity = EntityComment { + Author: "1", + CreatedAt: "2", + Guid: "3", + ParentGuid: "4", + Text: "5", + AuthorSignature: "6", + } + + var tests = []struct { + Order string + Expected string + }{ + { + Order: "author parent_guid", + Expected: "1;4", + }, + { + Order: "author parent_guid text guid", + Expected: "1;4;5;3", + }, + { + Order: "", + Expected: "", + }, + } + + for i, test := range tests { + result := ExractSignatureText(test.Order, entity) + if strings.Join(result, ";") != test.Expected { + t.Errorf("#%d: Expected to be '%s', got '%s'", i, test.Expected, strings.Join(result, ";")) + } + } +} diff --git a/salmon_test.go b/salmon_test.go index f2d4fb5..dd509f9 100644 --- a/salmon_test.go +++ b/salmon_test.go @@ -32,12 +32,13 @@ func TestParseDecryptedRequest(t *testing.T) { t.Errorf("Expected to be an error, got nil") } - _, entity, err = ParseDecryptedRequest(xml) + message, entity, err := ParseDecryptedRequest(xml) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } - parseRequest(t, entity) + parseEntityRequest(t, entity) + parseMessageRequest(t, message) } func TestParseEncryptedRequest(t *testing.T) { @@ -51,12 +52,13 @@ func TestParseEncryptedRequest(t *testing.T) { t.Errorf("Some error occured while parsing: %v", err) } - _, entity, err := ParseEncryptedRequest(wrapper, privKey) + message, entity, err := ParseEncryptedRequest(wrapper, privKey) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } - parseRequest(t, entity) + parseEntityRequest(t, entity) + parseMessageRequest(t, message) w := wrapper w.AesKey = "" @@ -73,7 +75,7 @@ func TestParseEncryptedRequest(t *testing.T) { } } -func parseRequest(t *testing.T, entity Entity) { +func parseEntityRequest(t *testing.T, entity Entity) { // {XMLName:{Space: Local:} Type:status_message SignatureOrder:author guid created_at text public Data:{XMLName:{Space: Local:status_message} Author:diaspora_2nd@localhost:3001 Guid:fe2d2a8053480135d08f5296f2e7447b CreatedAt:2017-07-25 09:24:33 +0000 UTC ProviderName: Text:ping Photo: Location: Poll: Public:false Event:}} if entity.Type != "status_message" { t.Errorf("Expected type string 'status_message', got '%s'", entity.Type) @@ -87,3 +89,41 @@ func parseRequest(t *testing.T, entity Entity) { t.Errorf("Expected the struct type EntityStatusMessage, got %+v", entity.Data) } } + +func parseMessageRequest(t *testing.T, message Message) { + var data = `PHN0YXR1c19tZXNzYWdlPgogIDxhdXRob3I-ZGlhc3BvcmFfMm5kQGxvY2FsaG9zdDozMDAxPC9hdXRob3I-CiAgPGd1aWQ-ZmUyZDJhODA1MzQ4MDEzNWQwOGY1Mjk2ZjJlNzQ0N2I8L2d1aWQ-CiAgPGNyZWF0ZWRfYXQ-MjAxNy0wNy0yNVQwOToyNDozM1o8L2NyZWF0ZWRfYXQ-CiAgPHByb3ZpZGVyX2Rpc3BsYXlfbmFtZS8-CiAgPHRleHQ-cGluZzwvdGV4dD4KICA8cHVibGljPmZhbHNlPC9wdWJsaWM-Cjwvc3RhdHVzX21lc3NhZ2U-` + + var sig = `NbuD4kERZzXPFRORH4NOcr7EAij-dWKTCG0eBBGZObN3Aic0lMAZ_rLU7o6PLOH9Q6p6dyneYjUjSu07vtI5Jy_N2XQpKUni3fUWxfDNgfMo26XKmxdJ5S2Gp1ux1ToO3FY9RByTZw5HZRpOBAfRSgttTgiY5_Yu5D-BLcEm_94R6FMWRniQXrMAt8hU9qCNSuVQlUKtuuy8qJXu6Z21VhI9lAT7wIALlR9UwIgz0e6UG9S9sU95f_38co0ibD1KbQpBd8c_lu5vCVIqlEe_Fa_xYZupMLaU8De-wzoBpBgqR65mRtUQTu2jP-Qxa3aXrANHxweIbnYfpZ5QcNA50hfyVJJSolczDSlDljTunEmHmWNaS3J7waEQsIDFATPFy6H5leRPpSzebXYca4T-EiapPP-mn41Vs3VKIdUXOHus_HcTPWRVT-Vr-yt7byFYEanb5b5lQ_IHcI0oyqX7RrVJid6UsBtwxwkX0FSc1cZgLhBQUgxBsUh5MNte-WZJv_6c9rHyNsH3rn9YEZp431P9GCe8gNdLY9bFQ1pYS9BxOAS2enu3yVpWpWRechiR7D__HC4-Hw2MHfSSmBQTxq5oO01_efEHB8XxWF85XYNT6_icXf3ZsTxkURT9HlHapkFwL7TlO5gPUZZVJt9f6kn9HoGQ56MX2n46KdKKid8=` + + if message.Me != XMLNS_ME { + t.Errorf("Expected to be %s, got %s", XMLNS_ME, message.Me) + } + + if message.Encoding != BASE64_URL { + t.Errorf("Expected to be %s, got %s", BASE64_URL, message.Encoding) + } + + if message.Alg != RSA_SHA256 { + t.Errorf("Expected to be %s, got %s", RSA_SHA256, message.Alg) + } + + if message.Data.Type != APPLICATION_XML { + t.Errorf("Expected to be %s, got %s", APPLICATION_XML, message.Data.Type) + } + + if message.Data.Data != data { + t.Errorf("Expected to be %s, got %s", data, message.Data.Data) + } + + if message.Sig.Sig != sig { + t.Errorf("Expected to be %s, got %s", sig, message.Sig.Sig) + } + + if message.Sig.KeyId != TEST_AUTHOR { + t.Errorf("Expected to be %s, got %s", TEST_AUTHOR, message.Sig.KeyId) + } + + if message.Signature() != `NbuD4kERZzXPFRORH4NOcr7EAij-dWKTCG0eBBGZObN3Aic0lMAZ_rLU7o6PLOH9Q6p6dyneYjUjSu07vtI5Jy_N2XQpKUni3fUWxfDNgfMo26XKmxdJ5S2Gp1ux1ToO3FY9RByTZw5HZRpOBAfRSgttTgiY5_Yu5D-BLcEm_94R6FMWRniQXrMAt8hU9qCNSuVQlUKtuuy8qJXu6Z21VhI9lAT7wIALlR9UwIgz0e6UG9S9sU95f_38co0ibD1KbQpBd8c_lu5vCVIqlEe_Fa_xYZupMLaU8De-wzoBpBgqR65mRtUQTu2jP-Qxa3aXrANHxweIbnYfpZ5QcNA50hfyVJJSolczDSlDljTunEmHmWNaS3J7waEQsIDFATPFy6H5leRPpSzebXYca4T-EiapPP-mn41Vs3VKIdUXOHus_HcTPWRVT-Vr-yt7byFYEanb5b5lQ_IHcI0oyqX7RrVJid6UsBtwxwkX0FSc1cZgLhBQUgxBsUh5MNte-WZJv_6c9rHyNsH3rn9YEZp431P9GCe8gNdLY9bFQ1pYS9BxOAS2enu3yVpWpWRechiR7D__HC4-Hw2MHfSSmBQTxq5oO01_efEHB8XxWF85XYNT6_icXf3ZsTxkURT9HlHapkFwL7TlO5gPUZZVJt9f6kn9HoGQ56MX2n46KdKKid8=` { + t.Errorf("Expected different signature, got '%s'", message.Signature()) + } +} diff --git a/signature_test.go b/signature_test.go new file mode 100644 index 0000000..64ce4cf --- /dev/null +++ b/signature_test.go @@ -0,0 +1,52 @@ +package federation +// +// GangGo Diaspora Federation Library +// Copyright (C) 2017 Lukas Matt +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import ( + "testing" +) + +func TestSignatureInterface(t *testing.T) { + var signature Signature + var sig string + + priv, err := ParseRSAPrivateKey(TEST_PRIV_KEY) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + + err = signature.New(EntityLike{}).Sign(priv, &sig) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + + if !signature.New(EntityLike{AuthorSignature: sig}).Verify( + "positive guid parent_guid parent_type author", &priv.PublicKey) { + t.Errorf("Expected to be a valid signature, got invalid") + } + + err = signature.New(EntityComment{}).Sign(priv, &sig) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + + if !signature.New(EntityComment{AuthorSignature: sig}).Verify( + "author created_at guid parent_guid text", &priv.PublicKey) { + t.Errorf("Expected to be a valid signature, got invalid") + } +} -- GitLab