gb vendor update github.com/matrix-org/gomatrixserverlib
parent
d118eced54
commit
e2f88f3889
|
@ -135,7 +135,7 @@
|
|||
{
|
||||
"importpath": "github.com/matrix-org/gomatrixserverlib",
|
||||
"repository": "https://github.com/matrix-org/gomatrixserverlib",
|
||||
"revision": "8540d3dfc13c797cd3200640bc06e0286ab355aa",
|
||||
"revision": "afa71391f946312c40639a419045e06b8ff2309a",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -53,6 +53,12 @@ func (b64 Base64String) MarshalJSON() ([]byte, error) {
|
|||
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.
|
||||
// This takes a pointer receiver because it needs to write the result of decoding.
|
||||
func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) {
|
||||
|
@ -65,3 +71,14 @@ func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) {
|
|||
err = b64.Decode(str)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package gomatrixserverlib
|
|||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func newFederationTripper() *federationTripper {
|
|||
ServerName: "",
|
||||
// TODO: We should be checking that the TLS certificate we see here matches
|
||||
// one of the allowed SHA-256 fingerprints for the server.
|
||||
InsecureSkipVerify: true,
|
||||
InsecureSkipVerify: true, // nolint: gas
|
||||
})
|
||||
if err := conn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
@ -306,6 +307,33 @@ func (e Event) SetUnsigned(unsigned interface{}) (Event, error) {
|
|||
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.
|
||||
// The reference can be used to refer to this event from other events.
|
||||
func (e Event) EventReference() EventReference {
|
||||
|
|
|
@ -50,3 +50,32 @@ func BenchmarkParseSmallerEventFailedHash(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}`)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// signatures from the server that sent it.
|
||||
func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVerifier) error { // nolint: gocyclo
|
||||
var toVerify []VerifyJSONRequest
|
||||
for _, event := range events {
|
||||
//
|
||||
// returns an array with either an error or nil for each event.
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains := make(map[ServerName]bool)
|
||||
|
@ -203,7 +212,7 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
|
|||
//
|
||||
senderDomain, err := domainFromID(event.Sender())
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
domains[ServerName(senderDomain)] = true
|
||||
|
||||
|
@ -212,12 +221,12 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
|
|||
if event.Type() == MRoomMember && event.StateKey() != nil {
|
||||
targetDomain, err := domainFromID(*event.StateKey())
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if ServerName(targetDomain) != event.Origin() {
|
||||
c, err := newMemberContentFromEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if c.Membership == invite {
|
||||
domains[ServerName(targetDomain)] = true
|
||||
|
@ -231,22 +240,45 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVeri
|
|||
AtTS: event.OriginServerTS(),
|
||||
ServerName: domain,
|
||||
}
|
||||
verificationMap[evtIdx] = append(verificationMap[evtIdx], len(toVerify))
|
||||
toVerify = append(toVerify, v)
|
||||
}
|
||||
}
|
||||
|
||||
results, err := keyRing.VerifyJSONs(ctx, toVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that all the event JSON was correctly signed.
|
||||
for _, result := range results {
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Everything was okay.
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
for idx := range events {
|
||||
ve := verificationErrors[idx]
|
||||
if ve != nil {
|
||||
return ve
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -272,8 +272,10 @@ func (v *StubVerifier) VerifyJSONs(ctx context.Context, requests []VerifyJSONReq
|
|||
return v.results, nil
|
||||
}
|
||||
|
||||
func TestVerifyEventSignatures(t *testing.T) {
|
||||
verifier := StubVerifier{}
|
||||
func TestVerifyAllEventSignatures(t *testing.T) {
|
||||
verifier := StubVerifier{
|
||||
results: make([]VerifyJSONResult, 2),
|
||||
}
|
||||
|
||||
eventJSON := []byte(`{
|
||||
"type": "m.room.name",
|
||||
|
@ -295,7 +297,7 @@ func TestVerifyEventSignatures(t *testing.T) {
|
|||
event.eventJSON = eventJSON
|
||||
|
||||
events := []Event{event}
|
||||
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||
if err := VerifyAllEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -329,8 +331,10 @@ func TestVerifyEventSignatures(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVerifyEventSignaturesForInvite(t *testing.T) {
|
||||
verifier := StubVerifier{}
|
||||
func TestVerifyAllEventSignaturesForInvite(t *testing.T) {
|
||||
verifier := StubVerifier{
|
||||
results: make([]VerifyJSONResult, 2),
|
||||
}
|
||||
|
||||
eventJSON := []byte(`{
|
||||
"type": "m.room.member",
|
||||
|
@ -352,7 +356,7 @@ func TestVerifyEventSignaturesForInvite(t *testing.T) {
|
|||
event.eventJSON = eventJSON
|
||||
|
||||
events := []Event{event}
|
||||
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||
if err := VerifyAllEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package gomatrixserverlib
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
@ -22,7 +21,7 @@ func NewFederationClient(
|
|||
serverName ServerName, keyID KeyID, privateKey ed25519.PrivateKey,
|
||||
) *FederationClient {
|
||||
return &FederationClient{
|
||||
Client: Client{client: http.Client{Transport: newFederationTripper()}},
|
||||
Client: *NewClient(),
|
||||
serverName: serverName,
|
||||
serverKeyID: keyID,
|
||||
serverPrivateKey: privateKey,
|
||||
|
|
|
@ -138,7 +138,7 @@ func (r RespState) Check(ctx context.Context, keyRing JSONVerifier) error {
|
|||
|
||||
// Check if the events pass signature checks.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -110,8 +110,10 @@ func FetchKeysDirect(serverName ServerName, addr, sni string) (*ServerKeys, *tls
|
|||
}
|
||||
defer tcpconn.Close() // nolint: errcheck
|
||||
tlsconn := tls.Client(tcpconn, &tls.Config{
|
||||
ServerName: sni,
|
||||
InsecureSkipVerify: true, // This must be specified even though the TLS library will ignore it.
|
||||
ServerName: sni,
|
||||
|
||||
// This must be specified even though the TLS library will ignore it.
|
||||
InsecureSkipVerify: true, // nolint: gas
|
||||
})
|
||||
if err = tlsconn.Handshake(); err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue