gb vendor update github.com/matrix-org/gomatrixserverlib

main
Richard van der Hoff 2017-12-08 17:56:34 +00:00
parent d118eced54
commit e2f88f3889
12 changed files with 214 additions and 26 deletions

2
vendor/manifest vendored
View File

@ -135,7 +135,7 @@
{ {
"importpath": "github.com/matrix-org/gomatrixserverlib", "importpath": "github.com/matrix-org/gomatrixserverlib",
"repository": "https://github.com/matrix-org/gomatrixserverlib", "repository": "https://github.com/matrix-org/gomatrixserverlib",
"revision": "8540d3dfc13c797cd3200640bc06e0286ab355aa", "revision": "afa71391f946312c40639a419045e06b8ff2309a",
"branch": "master" "branch": "master"
}, },
{ {

View File

@ -53,6 +53,12 @@ func (b64 Base64String) MarshalJSON() ([]byte, error) {
return json.Marshal(b64.Encode()) return json.Marshal(b64.Encode())
} }
// MarshalYAML implements yaml.Marshaller
// It just encodes the bytes as base64, which is a valid YAML string
func (b64 Base64String) MarshalYAML() (interface{}, error) {
return b64.Encode(), nil
}
// UnmarshalJSON decodes a JSON string and then decodes the resulting base64. // UnmarshalJSON decodes a JSON string and then decodes the resulting base64.
// This takes a pointer receiver because it needs to write the result of decoding. // This takes a pointer receiver because it needs to write the result of decoding.
func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) { func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) {
@ -65,3 +71,14 @@ func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) {
err = b64.Decode(str) err = b64.Decode(str)
return return
} }
// UnmarshalYAML implements yaml.Unmarshaller
// it unmarshals the input as a yaml string and then base64-decodes the result
func (b64 *Base64String) UnmarshalYAML(unmarshal func(interface{}) error) (err error) {
var str string
if err = unmarshal(&str); err != nil {
return
}
err = b64.Decode(str)
return
}

View File

@ -18,6 +18,8 @@ package gomatrixserverlib
import ( import (
"encoding/json" "encoding/json"
"testing" "testing"
"gopkg.in/yaml.v2"
) )
func TestMarshalBase64(t *testing.T) { func TestMarshalBase64(t *testing.T) {
@ -93,3 +95,58 @@ func TestMarshalBase64Slice(t *testing.T) {
t.Fatalf("json.Marshal(%v): wanted %q got %q", input, want, string(got)) t.Fatalf("json.Marshal(%v): wanted %q got %q", input, want, string(got))
} }
} }
func TestMarshalYAMLBase64(t *testing.T) {
input := Base64String("this\xffis\xffa\xfftest")
want := "dGhpc/9pc/9h/3Rlc3Q\n"
got, err := yaml.Marshal(input)
if err != nil {
t.Fatal(err)
}
if string(got) != want {
t.Fatalf("yaml.Marshal(%v): wanted %q got %q", input, want, string(got))
}
}
func TestMarshalYAMLBase64Struct(t *testing.T) {
input := struct{ Value Base64String }{Base64String("this\xffis\xffa\xfftest")}
want := "value: dGhpc/9pc/9h/3Rlc3Q\n"
got, err := yaml.Marshal(input)
if err != nil {
t.Fatal(err)
}
if string(got) != want {
t.Fatalf("yaml.Marshal(%v): wanted %q got %q", input, want, string(got))
}
}
func TestUnmarshalYAMLBase64(t *testing.T) {
input := []byte("dGhpc/9pc/9h/3Rlc3Q")
want := Base64String("this\xffis\xffa\xfftest")
var got Base64String
err := yaml.Unmarshal(input, &got)
if err != nil {
t.Fatal(err)
}
if string(got) != string(want) {
t.Fatalf("yaml.Unmarshal(%q): wanted %q got %q", string(input), want, string(got))
}
}
func TestUnmarshalYAMLBase64Struct(t *testing.T) {
// var u yaml.Unmarshaler
u := Base64String("this\xffis\xffa\xfftest")
input := []byte(`value: dGhpc/9pc/9h/3Rlc3Q`)
want := struct{ Value Base64String }{u}
result := struct {
Value Base64String `yaml:"value"`
}{}
err := yaml.Unmarshal(input, &result)
if err != nil {
t.Fatal(err)
}
if string(result.Value) != string(want.Value) {
t.Fatalf("yaml.Unmarshal(%v): wanted %q got %q", input, want, result)
}
}

View File

@ -80,7 +80,7 @@ func newFederationTripper() *federationTripper {
ServerName: "", ServerName: "",
// TODO: We should be checking that the TLS certificate we see here matches // TODO: We should be checking that the TLS certificate we see here matches
// one of the allowed SHA-256 fingerprints for the server. // one of the allowed SHA-256 fingerprints for the server.
InsecureSkipVerify: true, InsecureSkipVerify: true, // nolint: gas
}) })
if err := conn.Handshake(); err != nil { if err := conn.Handshake(); err != nil {
return nil, err return nil, err

View File

@ -22,6 +22,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson" "github.com/tidwall/sjson"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
) )
@ -306,6 +307,33 @@ func (e Event) SetUnsigned(unsigned interface{}) (Event, error) {
return result, nil return result, nil
} }
// SetUnsignedField takes a path and value to insert into the unsigned dict of
// the event.
// path is a dot separated path into the unsigned dict (see gjson package
// for details on format). In particular some characters like '.' and '*' must
// be escaped.
func (e *Event) SetUnsignedField(path string, value interface{}) error {
// The safest way is to change the unsigned json and then reparse the
// event fully. But since we are only changing the unsigned section,
// which doesn't affect the signatures or hashes, we can cheat and
// just fiddle those bits directly.
path = "unsigned." + path
eventJSON, err := sjson.SetBytes(e.eventJSON, path, value)
if err != nil {
return err
}
eventJSON = CanonicalJSONAssumeValid(eventJSON)
res := gjson.GetBytes(eventJSON, "unsigned")
unsigned := rawJSONFromResult(res, eventJSON)
e.eventJSON = eventJSON
e.fields.Unsigned = unsigned
return nil
}
// EventReference returns an EventReference for the event. // EventReference returns an EventReference for the event.
// The reference can be used to refer to this event from other events. // The reference can be used to refer to this event from other events.
func (e Event) EventReference() EventReference { func (e Event) EventReference() EventReference {

View File

@ -50,3 +50,32 @@ func BenchmarkParseSmallerEventFailedHash(b *testing.B) {
func BenchmarkParseSmallerEventRedacted(b *testing.B) { func BenchmarkParseSmallerEventRedacted(b *testing.B) {
benchmarkParse(b, `{"event_id":"$yvN1b43rlmcOs5fY:localhost","sender":"@test:localhost","room_id":"!19Mp0U9hjajeIiw1:localhost","hashes":{"sha256":"Oh1mwI1jEqZ3tgJ+V1Dmu5nOEGpCE4RFUqyJv2gQXKs"},"signatures":{"localhost":{"ed25519:u9kP":"5IzSuRXkxvbTp0vZhhXYZeOe+619iG3AybJXr7zfNn/4vHz4TH7qSJVQXSaHHvcTcDodAKHnTG1WDulgO5okAQ"}},"content":{},"type":"m.room.name","state_key":"","depth":7,"prev_events":[["$FqI6TVvWpcbcnJ97:localhost",{"sha256":"upCsBqUhNUgT2/+zkzg8TbqdQpWWKQnZpGJc6KcbUC4"}]],"prev_state":[],"auth_events":[["$oXL79cT7fFxR7dPH:localhost",{"sha256":"abjkiDSg1RkuZrbj2jZoGMlQaaj1Ue3Jhi7I7NlKfXY"}],["$IVUsaSkm1LBAZYYh:localhost",{"sha256":"X7RUj46hM/8sUHNBIFkStbOauPvbDzjSdH4NibYWnko"}],["$VS2QT0EeArZYi8wf:localhost",{"sha256":"k9eM6utkCH8vhLW9/oRsH74jOBS/6RVK42iGDFbylno"}]],"origin":"localhost","origin_server_ts":1510854416361}`) benchmarkParse(b, `{"event_id":"$yvN1b43rlmcOs5fY:localhost","sender":"@test:localhost","room_id":"!19Mp0U9hjajeIiw1:localhost","hashes":{"sha256":"Oh1mwI1jEqZ3tgJ+V1Dmu5nOEGpCE4RFUqyJv2gQXKs"},"signatures":{"localhost":{"ed25519:u9kP":"5IzSuRXkxvbTp0vZhhXYZeOe+619iG3AybJXr7zfNn/4vHz4TH7qSJVQXSaHHvcTcDodAKHnTG1WDulgO5okAQ"}},"content":{},"type":"m.room.name","state_key":"","depth":7,"prev_events":[["$FqI6TVvWpcbcnJ97:localhost",{"sha256":"upCsBqUhNUgT2/+zkzg8TbqdQpWWKQnZpGJc6KcbUC4"}]],"prev_state":[],"auth_events":[["$oXL79cT7fFxR7dPH:localhost",{"sha256":"abjkiDSg1RkuZrbj2jZoGMlQaaj1Ue3Jhi7I7NlKfXY"}],["$IVUsaSkm1LBAZYYh:localhost",{"sha256":"X7RUj46hM/8sUHNBIFkStbOauPvbDzjSdH4NibYWnko"}],["$VS2QT0EeArZYi8wf:localhost",{"sha256":"k9eM6utkCH8vhLW9/oRsH74jOBS/6RVK42iGDFbylno"}]],"origin":"localhost","origin_server_ts":1510854416361}`)
} }
func TestAddUnsignedField(t *testing.T) {
initialEventJSON := `{"auth_events":[["$oXL79cT7fFxR7dPH:localhost",{"sha256":"abjkiDSg1RkuZrbj2jZoGMlQaaj1Ue3Jhi7I7NlKfXY"}],["$IVUsaSkm1LBAZYYh:localhost",{"sha256":"X7RUj46hM/8sUHNBIFkStbOauPvbDzjSdH4NibYWnko"}],["$VS2QT0EeArZYi8wf:localhost",{"sha256":"k9eM6utkCH8vhLW9/oRsH74jOBS/6RVK42iGDFbylno"}]],"content":{"name":"test3"},"depth":7,"event_id":"$yvN1b43rlmcOs5fY:localhost","hashes":{"sha256":"Oh1mwI1jEqZ3tgJ+V1Dmu5nOEGpCE4RFUqyJv2gQXKs"},"origin":"localhost","origin_server_ts":1510854416361,"prev_events":[["$FqI6TVvWpcbcnJ97:localhost",{"sha256":"upCsBqUhNUgT2/+zkzg8TbqdQpWWKQnZpGJc6KcbUC4"}]],"prev_state":[],"room_id":"!19Mp0U9hjajeIiw1:localhost","sender":"@test:localhost","signatures":{"localhost":{"ed25519:u9kP":"5IzSuRXkxvbTp0vZhhXYZeOe+619iG3AybJXr7zfNn/4vHz4TH7qSJVQXSaHHvcTcDodAKHnTG1WDulgO5okAQ"}},"state_key":"","type":"m.room.name"}`
expectedEventJSON := `{"auth_events":[["$oXL79cT7fFxR7dPH:localhost",{"sha256":"abjkiDSg1RkuZrbj2jZoGMlQaaj1Ue3Jhi7I7NlKfXY"}],["$IVUsaSkm1LBAZYYh:localhost",{"sha256":"X7RUj46hM/8sUHNBIFkStbOauPvbDzjSdH4NibYWnko"}],["$VS2QT0EeArZYi8wf:localhost",{"sha256":"k9eM6utkCH8vhLW9/oRsH74jOBS/6RVK42iGDFbylno"}]],"content":{"name":"test3"},"depth":7,"event_id":"$yvN1b43rlmcOs5fY:localhost","hashes":{"sha256":"Oh1mwI1jEqZ3tgJ+V1Dmu5nOEGpCE4RFUqyJv2gQXKs"},"origin":"localhost","origin_server_ts":1510854416361,"prev_events":[["$FqI6TVvWpcbcnJ97:localhost",{"sha256":"upCsBqUhNUgT2/+zkzg8TbqdQpWWKQnZpGJc6KcbUC4"}]],"prev_state":[],"room_id":"!19Mp0U9hjajeIiw1:localhost","sender":"@test:localhost","signatures":{"localhost":{"ed25519:u9kP":"5IzSuRXkxvbTp0vZhhXYZeOe+619iG3AybJXr7zfNn/4vHz4TH7qSJVQXSaHHvcTcDodAKHnTG1WDulgO5okAQ"}},"state_key":"","type":"m.room.name","unsigned":{"foo":"bar","x":1}}`
var event Event
if err := json.Unmarshal([]byte(initialEventJSON), &event); err != nil {
t.Error("Failed to parse event")
}
err := event.SetUnsignedField("foo", "bar")
if err != nil {
t.Error("Failed to insert foo")
}
err = event.SetUnsignedField("x", 1)
if err != nil {
t.Error("Failed to insert x")
}
bytes, err := json.Marshal(event)
if err != nil {
t.Error("Failed to marshal x")
}
if expectedEventJSON != string(bytes) {
t.Fatalf("Serialized event does not match expected: %s != %s", string(bytes), initialEventJSON)
}
}

View File

@ -179,12 +179,21 @@ func verifyEventSignature(signingName string, keyID KeyID, publicKey ed25519.Pub
// VerifyEventSignatures checks that each event in a list of events has valid // VerifyEventSignatures checks that each event in a list of events has valid
// signatures from the server that sent it. // signatures from the server that sent it.
func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVerifier) error { // nolint: gocyclo //
var toVerify []VerifyJSONRequest // returns an array with either an error or nil for each event.
for _, event := range events { func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVerifier) ([]error, error) { // nolint: gocyclo
// we will end up doing at least as many verifications as we have events.
// some events require multiple verifications, as they are signed by multiple
// servers.
toVerify := make([]VerifyJSONRequest, 0, len(events))
// for each entry in 'events', a list of corresponding indexes in toVerify
verificationMap := make([][]int, len(events))
for evtIdx, event := range events {
redactedJSON, err := redactEvent(event.eventJSON) redactedJSON, err := redactEvent(event.eventJSON)
if err != nil { if err != nil {
return err return nil, err
} }
domains := make(map[ServerName]bool) domains := make(map[ServerName]bool)
@ -203,7 +212,7 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
// //
senderDomain, err := domainFromID(event.Sender()) senderDomain, err := domainFromID(event.Sender())
if err != nil { if err != nil {
return err return nil, err
} }
domains[ServerName(senderDomain)] = true domains[ServerName(senderDomain)] = true
@ -212,12 +221,12 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
if event.Type() == MRoomMember && event.StateKey() != nil { if event.Type() == MRoomMember && event.StateKey() != nil {
targetDomain, err := domainFromID(*event.StateKey()) targetDomain, err := domainFromID(*event.StateKey())
if err != nil { if err != nil {
return err return nil, err
} }
if ServerName(targetDomain) != event.Origin() { if ServerName(targetDomain) != event.Origin() {
c, err := newMemberContentFromEvent(event) c, err := newMemberContentFromEvent(event)
if err != nil { if err != nil {
return err return nil, err
} }
if c.Membership == invite { if c.Membership == invite {
domains[ServerName(targetDomain)] = true domains[ServerName(targetDomain)] = true
@ -231,22 +240,45 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
AtTS: event.OriginServerTS(), AtTS: event.OriginServerTS(),
ServerName: domain, ServerName: domain,
} }
verificationMap[evtIdx] = append(verificationMap[evtIdx], len(toVerify))
toVerify = append(toVerify, v) toVerify = append(toVerify, v)
} }
} }
results, err := keyRing.VerifyJSONs(ctx, toVerify) results, err := keyRing.VerifyJSONs(ctx, toVerify)
if err != nil {
return nil, err
}
// Check that all the event JSON was correctly signed
verificationErrors := make([]error, len(events))
for evtIdx := range events {
for _, verificationIdx := range verificationMap[evtIdx] {
result := results[verificationIdx]
if result.Error != nil {
verificationErrors[evtIdx] = result.Error
break // break inner loop; continue with outer
}
}
}
return verificationErrors, nil
}
// VerifyAllEventSignatures checks that each event in a list of events has valid
// signatures from the server that sent it.
//
// returns an error if any event fails verifications
func VerifyAllEventSignatures(ctx context.Context, events []Event, keyRing JSONVerifier) error {
verificationErrors, err := VerifyEventSignatures(ctx, events, keyRing)
if err != nil { if err != nil {
return err return err
} }
for idx := range events {
// Check that all the event JSON was correctly signed. ve := verificationErrors[idx]
for _, result := range results { if ve != nil {
if result.Error != nil { return ve
return result.Error
} }
} }
// Everything was okay.
return nil return nil
} }

View File

@ -272,8 +272,10 @@ func (v *StubVerifier) VerifyJSONs(ctx context.Context, requests []VerifyJSONReq
return v.results, nil return v.results, nil
} }
func TestVerifyEventSignatures(t *testing.T) { func TestVerifyAllEventSignatures(t *testing.T) {
verifier := StubVerifier{} verifier := StubVerifier{
results: make([]VerifyJSONResult, 2),
}
eventJSON := []byte(`{ eventJSON := []byte(`{
"type": "m.room.name", "type": "m.room.name",
@ -295,7 +297,7 @@ func TestVerifyEventSignatures(t *testing.T) {
event.eventJSON = eventJSON event.eventJSON = eventJSON
events := []Event{event} events := []Event{event}
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil { if err := VerifyAllEventSignatures(context.Background(), events, &verifier); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -329,8 +331,10 @@ func TestVerifyEventSignatures(t *testing.T) {
} }
} }
func TestVerifyEventSignaturesForInvite(t *testing.T) { func TestVerifyAllEventSignaturesForInvite(t *testing.T) {
verifier := StubVerifier{} verifier := StubVerifier{
results: make([]VerifyJSONResult, 2),
}
eventJSON := []byte(`{ eventJSON := []byte(`{
"type": "m.room.member", "type": "m.room.member",
@ -352,7 +356,7 @@ func TestVerifyEventSignaturesForInvite(t *testing.T) {
event.eventJSON = eventJSON event.eventJSON = eventJSON
events := []Event{event} events := []Event{event}
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil { if err := VerifyAllEventSignatures(context.Background(), events, &verifier); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -2,7 +2,6 @@ package gomatrixserverlib
import ( import (
"context" "context"
"net/http"
"net/url" "net/url"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
@ -22,7 +21,7 @@ func NewFederationClient(
serverName ServerName, keyID KeyID, privateKey ed25519.PrivateKey, serverName ServerName, keyID KeyID, privateKey ed25519.PrivateKey,
) *FederationClient { ) *FederationClient {
return &FederationClient{ return &FederationClient{
Client: Client{client: http.Client{Transport: newFederationTripper()}}, Client: *NewClient(),
serverName: serverName, serverName: serverName,
serverKeyID: keyID, serverKeyID: keyID,
serverPrivateKey: privateKey, serverPrivateKey: privateKey,

View File

@ -138,7 +138,7 @@ func (r RespState) Check(ctx context.Context, keyRing JSONVerifier) error {
// Check if the events pass signature checks. // Check if the events pass signature checks.
logger.Infof("Checking event signatures for %d events of room state", len(allEvents)) logger.Infof("Checking event signatures for %d events of room state", len(allEvents))
if err := VerifyEventSignatures(ctx, allEvents, keyRing); err != nil { if err := VerifyAllEventSignatures(ctx, allEvents, keyRing); err != nil {
return err return err
} }

View File

@ -111,7 +111,9 @@ func FetchKeysDirect(serverName ServerName, addr, sni string) (*ServerKeys, *tls
defer tcpconn.Close() // nolint: errcheck defer tcpconn.Close() // nolint: errcheck
tlsconn := tls.Client(tcpconn, &tls.Config{ tlsconn := tls.Client(tcpconn, &tls.Config{
ServerName: sni, ServerName: sni,
InsecureSkipVerify: true, // This must be specified even though the TLS library will ignore it.
// This must be specified even though the TLS library will ignore it.
InsecureSkipVerify: true, // nolint: gas
}) })
if err = tlsconn.Handshake(); err != nil { if err = tlsconn.Handshake(); err != nil {
return nil, nil, err return nil, nil, err

View File

@ -0,0 +1,20 @@
#!/bin/sh
set -eux
cd `dirname $0`
# -u so that if this is run on a dev box, we get the latest deps, as
# we do on travis.
go get -u \
github.com/alecthomas/gometalinter \
golang.org/x/crypto/ed25519 \
github.com/matrix-org/util \
github.com/matrix-org/gomatrix \
github.com/tidwall/gjson \
github.com/tidwall/sjson \
github.com/pkg/errors \
gopkg.in/yaml.v2 \
./hooks/pre-commit