Skip to content

Commit 9fb870e

Browse files
authored
Remove libolm dependency (#738)
* Remove libolm dependency See #729 for more information. This is a breaking change because it removes blueprint functionality. Specifically, it removes: - the field `OneTimeKeys` from the `User` struct. - the performance blueprint `BlueprintPerfE2EERoom`. * Remove olm dep in client.go * Fix device list test
1 parent 5b72981 commit 9fb870e

File tree

8 files changed

+72
-204
lines changed

8 files changed

+72
-204
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ jobs:
1818
- uses: actions/setup-go@v4
1919
with:
2020
go-version-file: go.mod
21-
- name: "Install Complement Dependencies"
22-
run: |
23-
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
2421
- name: "Run internal Complement tests"
2522
run: |
2623
go test ./internal/...
@@ -69,7 +66,6 @@ jobs:
6966
# servers which listen on random high numbered ports.
7067
- name: "Install Complement Dependencies"
7168
run: |
72-
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
7369
go install -v github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
7470
mkdir .gotestfmt/github -p
7571
cp .ci/complement_package.gotpl .gotestfmt/github/package.gotpl

b/blueprints.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ var KnownBlueprints = map[string]*Blueprint{
3232
BlueprintOneToOneRoom.Name: &BlueprintOneToOneRoom,
3333
BlueprintPerfManyMessages.Name: &BlueprintPerfManyMessages,
3434
BlueprintPerfManyRooms.Name: &BlueprintPerfManyRooms,
35-
BlueprintPerfE2EERoom.Name: &BlueprintPerfE2EERoom,
3635
}
3736

3837
// Blueprint represents an entire deployment to make.
@@ -64,10 +63,6 @@ type User struct {
6463
AvatarURL string
6564
AccountData []AccountData
6665
DeviceID *string
67-
// Enable end-to end encryption for this user and upload the given
68-
// amount of one-time keys. This requires the DeviceId to be set as
69-
// well.
70-
OneTimeKeys uint
7166
}
7267

7368
type AccountData struct {

b/perf_e2ee_room.go

Lines changed: 0 additions & 104 deletions
This file was deleted.

client/client.go

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package client
33
import (
44
"bytes"
55
"context" // nolint:gosec
6+
"crypto/ed25519"
7+
"encoding/base64"
68
"encoding/json"
79
"fmt"
810
"io"
11+
"math/rand"
912
"net/http"
1013
"net/http/httputil"
1114
"net/url"
@@ -16,7 +19,7 @@ import (
1619

1720
"github.com/matrix-org/gomatrixserverlib"
1821
"github.com/tidwall/gjson"
19-
"maunium.net/go/mautrix/crypto/olm"
22+
"golang.org/x/crypto/curve25519"
2023

2124
"github.com/matrix-org/complement/b"
2225
"github.com/matrix-org/complement/ct"
@@ -28,6 +31,12 @@ const (
2831
CtxKeyWithRetryUntil ctxKey = "complement_retry_until" // contains *retryUntilParams
2932
)
3033

34+
var (
35+
// use a deterministic seed but globally so we don't generate the same numbers for each client.
36+
// This could be non-deterministic if used concurrently.
37+
prng = rand.New(rand.NewSource(42))
38+
)
39+
3140
type retryUntilParams struct {
3241
timeout time.Duration
3342
untilFn func(*http.Response) bool
@@ -403,10 +412,21 @@ func (c *CSAPI) MustUploadKeys(t ct.TestLike, deviceKeys map[string]interface{},
403412
return s.OTKCounts
404413
}
405414

415+
// Generate realistic looking device keys and OTKs. They are not guaranteed to be 100% valid, but should
416+
// pass most server-side checks. Critically, these keys are generated using a Pseudo-Random Number Generator (PRNG)
417+
// for determinism and hence ARE NOT SECURE. DO NOT USE THIS OUTSIDE OF TESTS.
406418
func (c *CSAPI) MustGenerateOneTimeKeys(t ct.TestLike, otkCount uint) (deviceKeys map[string]interface{}, oneTimeKeys map[string]interface{}) {
407419
t.Helper()
408-
account := olm.NewAccount()
409-
ed25519Key, curveKey := account.IdentityKeys()
420+
ed25519PubKey, ed25519PrivKey, err := ed25519.GenerateKey(prng)
421+
if err != nil {
422+
ct.Fatalf(t, "failed to generate ed25519 key: %s", err)
423+
}
424+
425+
curveKey := make([]byte, 32)
426+
_, err = prng.Read(curveKey)
427+
if err != nil {
428+
ct.Fatalf(t, "failed to read from prng: %s", err)
429+
}
410430

411431
ed25519KeyID := fmt.Sprintf("ed25519:%s", c.DeviceID)
412432
curveKeyID := fmt.Sprintf("curve25519:%s", c.DeviceID)
@@ -416,38 +436,59 @@ func (c *CSAPI) MustGenerateOneTimeKeys(t ct.TestLike, otkCount uint) (deviceKey
416436
"device_id": c.DeviceID,
417437
"algorithms": []interface{}{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"},
418438
"keys": map[string]interface{}{
419-
ed25519KeyID: ed25519Key.String(),
420-
curveKeyID: curveKey.String(),
439+
ed25519KeyID: base64.RawStdEncoding.EncodeToString(ed25519PubKey),
440+
curveKeyID: base64.RawStdEncoding.EncodeToString(curveKey),
421441
},
422442
}
423443

424-
signature, _ := account.SignJSON(deviceKeys)
444+
signJSON := func(input any) []byte {
445+
inputJSON, err := json.Marshal(input)
446+
if err != nil {
447+
ct.Fatalf(t, "failed to marshal struct: %s", err)
448+
}
449+
inputJSON, err = gomatrixserverlib.CanonicalJSON(inputJSON)
450+
if err != nil {
451+
ct.Fatalf(t, "failed to canonical json: %s", err)
452+
}
453+
signature := ed25519.Sign(ed25519PrivKey, inputJSON)
454+
if err != nil {
455+
ct.Fatalf(t, "failed to sign json: %s", err)
456+
}
457+
return signature
458+
}
425459

426460
deviceKeys["signatures"] = map[string]interface{}{
427461
c.UserID: map[string]interface{}{
428-
ed25519KeyID: signature,
462+
ed25519KeyID: base64.RawStdEncoding.EncodeToString(signJSON(deviceKeys)),
429463
},
430464
}
431-
432-
account.GenOneTimeKeys(otkCount)
433465
oneTimeKeys = map[string]interface{}{}
434466

435-
for kid, key := range account.OneTimeKeys() {
467+
for i := uint(0); i < otkCount; i++ {
468+
privateKeyBytes := make([]byte, 32)
469+
_, err = prng.Read(privateKeyBytes)
470+
if err != nil {
471+
ct.Fatalf(t, "failed to read from prng", err)
472+
}
473+
key, err := curve25519.X25519(privateKeyBytes, curve25519.Basepoint)
474+
if err != nil {
475+
ct.Fatalf(t, "failed to generate curve pubkey: %s", err)
476+
}
477+
kid := fmt.Sprintf("%d", i)
436478
keyID := fmt.Sprintf("signed_curve25519:%s", kid)
437479
keyMap := map[string]interface{}{
438-
"key": key.String(),
480+
"key": base64.RawStdEncoding.EncodeToString(key),
439481
}
440482

441-
signature, _ = account.SignJSON(keyMap)
442-
443483
keyMap["signatures"] = map[string]interface{}{
444484
c.UserID: map[string]interface{}{
445-
ed25519KeyID: signature,
485+
ed25519KeyID: base64.RawStdEncoding.EncodeToString(signJSON(keyMap)),
446486
},
447487
}
448488

449489
oneTimeKeys[keyID] = keyMap
450490
}
491+
451492
return deviceKeys, oneTimeKeys
452493
}
453494

cmd/account-snapshot/internal/blueprint.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ func ConvertToBlueprint(s *Snapshot, serverName string) (*b.Blueprint, error) {
4040
DisplayName: local,
4141
DeviceID: &devices[i],
4242
}
43-
// set up OTKs if this user+device sends E2E messages
44-
if devices[i] != NoEncryptedDevice {
45-
user.OneTimeKeys = 100
46-
}
4743
// set DM list if this is the syncing user
4844
if userID == s.UserID {
4945
user.AccountData = append(user.AccountData, b.AccountData{

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ require (
1414
github.com/tidwall/sjson v1.2.5
1515
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
1616
gonum.org/v1/plot v0.11.0
17-
maunium.net/go/mautrix v0.11.0
1817
)
1918

2019
require (

internal/instruction/runner.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"time"
1717

1818
"github.com/tidwall/gjson"
19-
"maunium.net/go/mautrix/crypto/olm"
2019

2120
"github.com/matrix-org/complement/b"
2221
)
@@ -418,9 +417,6 @@ func calculateUserInstructionSets(r *Runner, hs b.Homeserver) [][]instruction {
418417
}
419418
createdUsers[user.Localpart] = true
420419

421-
if user.OneTimeKeys > 0 {
422-
instrs = append(instrs, instructionOneTimeKeyUpload(hs, user))
423-
}
424420
sets[i] = instrs
425421
}
426422
return sets
@@ -636,65 +632,6 @@ func instructionLogin(hs b.Homeserver, user b.User) instruction {
636632
}
637633
}
638634

639-
func instructionOneTimeKeyUpload(hs b.Homeserver, user b.User) instruction {
640-
account := olm.NewAccount()
641-
ed25519Key, curveKey := account.IdentityKeys()
642-
643-
userID := fmt.Sprintf("@%s:%s", user.Localpart, hs.Name)
644-
deviceID := *user.DeviceID
645-
646-
ed25519KeyID := fmt.Sprintf("ed25519:%s", deviceID)
647-
curveKeyID := fmt.Sprintf("curve25519:%s", deviceID)
648-
649-
deviceKeys := map[string]interface{}{
650-
"user_id": userID,
651-
"device_id": deviceID,
652-
"algorithms": []string{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"},
653-
"keys": map[string]string{
654-
ed25519KeyID: ed25519Key.String(),
655-
curveKeyID: curveKey.String(),
656-
},
657-
}
658-
659-
signature, _ := account.SignJSON(deviceKeys)
660-
661-
deviceKeys["signatures"] = map[string]map[string]string{
662-
userID: {
663-
ed25519KeyID: signature,
664-
},
665-
}
666-
667-
account.GenOneTimeKeys(user.OneTimeKeys)
668-
669-
oneTimeKeys := map[string]interface{}{}
670-
671-
for kid, key := range account.OneTimeKeys() {
672-
keyID := fmt.Sprintf("signed_curve25519:%s", kid)
673-
keyMap := map[string]interface{}{
674-
"key": key.String(),
675-
}
676-
677-
signature, _ = account.SignJSON(keyMap)
678-
679-
keyMap["signatures"] = map[string]interface{}{
680-
userID: map[string]string{
681-
ed25519KeyID: signature,
682-
},
683-
}
684-
685-
oneTimeKeys[keyID] = keyMap
686-
}
687-
return instruction{
688-
method: "POST",
689-
path: "/_matrix/client/v3/keys/upload",
690-
accessToken: fmt.Sprintf("user_@%s:%s", user.Localpart, hs.Name),
691-
body: map[string]interface{}{
692-
"device_keys": deviceKeys,
693-
"one_time_keys": oneTimeKeys,
694-
},
695-
}
696-
}
697-
698635
// indexFor hashes the input and returns a number % numEntries
699636
func indexFor(input string, numEntries int) int {
700637
hh := fnv.New32a()

0 commit comments

Comments
 (0)