Skip to content

Commit ec318c0

Browse files
committed
Move verify_signature_json to new file and add test
1 parent 5e3dbf5 commit ec318c0

File tree

6 files changed

+91
-41
lines changed

6 files changed

+91
-41
lines changed

mautrix/crypto/base.py

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,18 @@
55
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
from __future__ import annotations
77

8-
from typing import Any, Awaitable, Callable, TypedDict
8+
from typing import Awaitable, Callable
99
import asyncio
10-
import functools
11-
import json
12-
13-
import olm
1410

1511
from mautrix.errors import MForbidden, MNotFound
1612
from mautrix.types import (
17-
DeviceID,
18-
EncryptionKeyAlgorithm,
1913
EventType,
2014
IdentityKey,
21-
KeyID,
2215
RequestedKeyInfo,
2316
RoomEncryptionStateEventContent,
2417
RoomID,
2518
RoomKeyEventContent,
2619
SessionID,
27-
SigningKey,
2820
TrustState,
2921
UserID,
3022
)
@@ -34,11 +26,6 @@
3426
from .ssss import Machine as SSSSMachine
3527

3628

37-
class SignedObject(TypedDict):
38-
signatures: dict[UserID, dict[str, str]]
39-
unsigned: Any
40-
41-
4229
class BaseOlmMachine:
4330
client: cli.Client
4431
ssss: SSSSMachine
@@ -118,27 +105,3 @@ async def _fill_encryption_info(self, evt: RoomKeyEventContent) -> None:
118105
evt.beeper_max_age_ms = encryption_info.rotation_period_ms
119106
if not evt.beeper_max_messages:
120107
evt.beeper_max_messages = encryption_info.rotation_period_msgs
121-
122-
123-
canonical_json = functools.partial(
124-
json.dumps, ensure_ascii=False, separators=(",", ":"), sort_keys=True
125-
)
126-
127-
128-
def verify_signature_json(
129-
data: "SignedObject", user_id: UserID, key_name: DeviceID | str, key: SigningKey
130-
) -> bool:
131-
data_copy = {**data}
132-
data_copy.pop("unsigned", None)
133-
signatures = data_copy.pop("signatures")
134-
key_id = str(KeyID(EncryptionKeyAlgorithm.ED25519, key_name))
135-
try:
136-
signature = signatures[user_id][key_id]
137-
except KeyError:
138-
return False
139-
signed_data = canonical_json(data_copy)
140-
try:
141-
olm.ed25519_verify(key, signed_data, signature)
142-
return True
143-
except olm.OlmVerifyError:
144-
return False

mautrix/crypto/device_lists.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
UserID,
2424
)
2525

26-
from .base import BaseOlmMachine, verify_signature_json
26+
from .base import BaseOlmMachine
27+
from .signature import verify_signature_json
2728

2829

2930
class DeviceListMachine(BaseOlmMachine):

mautrix/crypto/encrypt_olm.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
UserID,
1919
)
2020

21-
from .base import BaseOlmMachine, verify_signature_json
21+
from .base import BaseOlmMachine
2222
from .sessions import Session
23+
from .signature import verify_signature_json
2324

2425
ClaimKeysList = Dict[UserID, Dict[DeviceID, DeviceIdentity]]
2526

mautrix/crypto/signature.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright (c) 2025 Tulir Asokan
2+
#
3+
# This Source Code Form is subject to the terms of the Mozilla Public
4+
# License, v. 2.0. If a copy of the MPL was not distributed with this
5+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
from typing import Any, TypedDict
7+
import functools
8+
import json
9+
10+
import olm
11+
12+
from mautrix.types import DeviceID, EncryptionKeyAlgorithm, KeyID, SigningKey, UserID
13+
14+
try:
15+
from Crypto.PublicKey import ECC
16+
from Crypto.Signature import eddsa
17+
except ImportError:
18+
from Cryptodome.PublicKey import ECC
19+
from Cryptodome.Signature import eddsa
20+
21+
canonical_json = functools.partial(
22+
json.dumps, ensure_ascii=False, separators=(",", ":"), sort_keys=True
23+
)
24+
25+
26+
class SignedObject(TypedDict):
27+
signatures: dict[UserID, dict[str, str]]
28+
unsigned: Any
29+
30+
31+
def verify_signature_json(
32+
data: "SignedObject", user_id: UserID, key_name: DeviceID | str, key: SigningKey
33+
) -> bool:
34+
data_copy = {**data}
35+
data_copy.pop("unsigned", None)
36+
signatures = data_copy.pop("signatures")
37+
key_id = str(KeyID(EncryptionKeyAlgorithm.ED25519, key_name))
38+
try:
39+
signature = signatures[user_id][key_id]
40+
except KeyError:
41+
return False
42+
signed_data = canonical_json(data_copy)
43+
try:
44+
olm.ed25519_verify(key, signed_data, signature)
45+
return True
46+
except olm.OlmVerifyError:
47+
return False

mautrix/crypto/signature_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright (c) 2025 Tulir Asokan
2+
#
3+
# This Source Code Form is subject to the terms of the Mozilla Public
4+
# License, v. 2.0. If a copy of the MPL was not distributed with this
5+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
from mautrix.types import SigningKey, UserID
7+
8+
from .signature import verify_signature_json
9+
10+
11+
def test_verify_signature_json() -> None:
12+
assert verify_signature_json(
13+
# This is actually a federation PDU rather than a device signature,
14+
# but they're both 25519 curves so it doesn't make a difference.
15+
{
16+
"auth_events": [
17+
"$L8Ak6A939llTRIsZrytMlLDXQhI4uLEjx-wb1zSg-Bw",
18+
"$QJmr7mmGeXGD4Tof0ZYSPW2oRGklseyHTKtZXnF-YNM",
19+
"$7bkKK_Z-cGQ6Ae4HXWGBwXyZi3YjC6rIcQzGfVyl3Eo",
20+
],
21+
"content": {},
22+
"depth": 3212,
23+
"hashes": {"sha256": "K549YdTnv62Jn84Y7sS5ZN3+AdmhleZHbenbhUpR2R8"},
24+
"origin_server_ts": 1754242687127,
25+
"prev_events": ["$DAhJg4jVsqk5FRatE2hbT1dSA8D2ASy5DbjEHIMSHwY"],
26+
"room_id": "!offtopic-2:continuwuity.org",
27+
"sender": "@tulir:maunium.net",
28+
"type": "m.room.message",
29+
"signatures": {
30+
UserID("maunium.net"): {
31+
"ed25519:a_xxeS": "SkzZdZ+rH22kzCBBIAErTdB0Vg6vkFmzvwjlOarGul72EnufgtE/tJcd3a8szAdK7f1ZovRyQxDgVm/Ib2u0Aw"
32+
}
33+
},
34+
"unsigned": {"age_ts": 1754242687146},
35+
},
36+
UserID("maunium.net"),
37+
"a_xxeS",
38+
SigningKey("lVt/CC3tv74OH6xTph2JrUmeRj/j+1q0HVa0Xf4QlCg"),
39+
)

mautrix/crypto/ssss/util.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
import hashlib
77
import hmac
8-
import struct
98

109
import base58
1110
import unpaddedbase64

0 commit comments

Comments
 (0)