Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-signing types, Base64Types updates #274

Merged
merged 15 commits into from
Aug 2, 2021
27 changes: 27 additions & 0 deletions base64.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package gomatrixserverlib

import (
"database/sql/driver"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)

Expand All @@ -26,6 +28,10 @@ import (
//
// The bytes encoded using base64 when marshalled as JSON.
// When the bytes are unmarshalled from JSON they are decoded from base64.
//
// When scanning directly from a database, a string column will be
// decoded from base64 automatically whereas a bytes column will be
// copied as-is.
type Base64Bytes []byte

// Encode encodes the bytes as base64
Expand All @@ -46,6 +52,27 @@ func (b64 *Base64Bytes) Decode(str string) error {
return err
}

// Implements sql.Scanner
func (b64 *Base64Bytes) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return b64.Decode(v)
case []byte:
new := append(Base64Bytes{}, v...)
b64 = &new
return nil
case RawJSON:
return b64.UnmarshalJSON(v)
default:
return fmt.Errorf("unsupported source type")
}
}

// Implements sql.Valuer
func (b64 Base64Bytes) Value() (driver.Value, error) {
return b64.Encode(), nil
}

// MarshalJSON encodes the bytes as base64 and then encodes the base64 as a JSON string.
// This takes a value receiver so that maps and slices of Base64Bytes encode correctly.
func (b64 Base64Bytes) MarshalJSON() ([]byte, error) {
Expand Down
37 changes: 37 additions & 0 deletions base64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package gomatrixserverlib

import (
"bytes"
"encoding/json"
"testing"

Expand Down Expand Up @@ -150,3 +151,39 @@ func TestUnmarshalYAMLBase64Struct(t *testing.T) {
t.Fatalf("yaml.Unmarshal(%v): wanted %q got %q", input, want, result)
}
}

func TestScanBase64(t *testing.T) {
expecting := Base64Bytes("This is a test string")

inputStr := "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n"
inputJSON := RawJSON(`"` + inputStr + `"`)
inputBytes := []byte(inputStr)
inputInt := 3

var b Base64Bytes

if err := b.Scan(inputStr); err != nil {
t.Fatal(err)
}
if !bytes.Equal(expecting, b) {
t.Fatalf("scanning from string failed, got %v, wanted %v", b, expecting)
}

if err := b.Scan(inputJSON); err != nil {
t.Fatal(err)
}
if !bytes.Equal(expecting, b) {
t.Fatalf("scanning from RawJSON failed, got %v, wanted %v", b, expecting)
}

if err := b.Scan(inputBytes); err != nil {
t.Fatal(err)
}
neilalexander marked this conversation as resolved.
Show resolved Hide resolved
if !bytes.Equal(expecting, b) {
t.Fatalf("scanning from []byte failed, got %v, wanted %v", b, expecting)
}

if err := b.Scan(inputInt); err == nil {
t.Fatal("scanning from int should have failed but didn't")
}
}
71 changes: 71 additions & 0 deletions crosssigning.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gomatrixserverlib

import (
"encoding/json"

"github.com/tidwall/gjson"
)

type CrossSigningKeyPurpose string

const (
CrossSigningKeyPurposeMaster CrossSigningKeyPurpose = "master"
CrossSigningKeyPurposeSelfSigning CrossSigningKeyPurpose = "self_signing"
CrossSigningKeyPurposeUserSigning CrossSigningKeyPurpose = "user_signing"
)

type CrossSigningKeys struct {
MasterKey CrossSigningKey `json:"master_key"`
SelfSigningKey CrossSigningKey `json:"self_signing_key"`
UserSigningKey CrossSigningKey `json:"user_signing_key"`
}

// https://spec.matrix.org/unstable/client-server-api/#post_matrixclientr0keysdevice_signingupload
type CrossSigningKey struct {
Signatures map[string]map[KeyID]Base64Bytes `json:"signatures,omitempty"`
Keys map[KeyID]Base64Bytes `json:"keys"`
Usage []CrossSigningKeyPurpose `json:"usage"`
UserID string `json:"user_id"`
}

func (s *CrossSigningKey) isCrossSigningBody() {} // implements CrossSigningBody

type CrossSigningBody interface {
isCrossSigningBody()
}

type CrossSigningForKeyOrDevice struct {
CrossSigningBody
}

// Implements json.Unmarshaler
func (c *CrossSigningForKeyOrDevice) UnmarshalJSON(b []byte) error {
if gjson.GetBytes(b, "device_id").Exists() {
body := &DeviceKeys{}
if err := json.Unmarshal(b, body); err != nil {
return err
}
c.CrossSigningBody = body
return nil
}
body := &CrossSigningKey{}
if err := json.Unmarshal(b, body); err != nil {
return err
}
c.CrossSigningBody = body
return nil
}
25 changes: 22 additions & 3 deletions federationtypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gomatrixserverlib

import (
"context"
"database/sql/driver"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -243,11 +244,11 @@ type RespUserDeviceKeys struct {
DeviceID string `json:"device_id"`
Algorithms []string `json:"algorithms"`
// E.g "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"
Keys map[string]string `json:"keys"`
Keys map[KeyID]Base64Bytes `json:"keys"`
// E.g "@alice:example.com": {
// "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
// }
Signatures map[string]map[string]string `json:"signatures"`
Signatures map[string]map[KeyID]Base64Bytes `json:"signatures"`
}

// UnmarshalJSON implements json.Unmarshaller
Expand Down Expand Up @@ -874,7 +875,9 @@ type RespClaimKeys struct {

// RespQueryKeys is the response for https://matrix.org/docs/spec/server_server/latest#post-matrix-federation-v1-user-keys-query
type RespQueryKeys struct {
DeviceKeys map[string]map[string]DeviceKeys `json:"device_keys"`
DeviceKeys map[string]map[string]DeviceKeys `json:"device_keys"`
MasterKeys map[string]CrossSigningForKeyOrDevice `json:"master_keys"`
SelfSigningKeys map[string]CrossSigningForKeyOrDevice `json:"self_signing_keys"`
}

// DeviceKeys as per https://matrix.org/docs/spec/server_server/latest#post-matrix-federation-v1-user-keys-query
Expand All @@ -885,6 +888,22 @@ type DeviceKeys struct {
Unsigned map[string]interface{} `json:"unsigned"`
}

func (s *DeviceKeys) isCrossSigningBody() {} // implements CrossSigningBody

func (s *DeviceKeys) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), s)
case []byte:
return json.Unmarshal(v, s)
}
return fmt.Errorf("unsupported source type")
}

func (s DeviceKeys) Value() (driver.Value, error) {
return json.Marshal(s)
}

// MSC2836EventRelationshipsRequest is a request to /event_relationships from
// https://github.com/matrix-org/matrix-doc/blob/kegan/msc/threading/proposals/2836-threading.md
// nolint:maligned
Expand Down