Skip to content

Commit 1be0976

Browse files
committed
Ensure nacl 1.4.0+; add tests for small subgroups
Several of the sodium bindings we use weren't added until nacl 1.4.0, so fail if we don't have that. (I've also pushed Debian's 1.5.0-2 python3-nacl package from sid to the focal oxen repo, which works just fine on focal).
1 parent d3464ab commit 1be0976

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

sogs/crypto.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44

5+
import nacl
56
from nacl.public import PrivateKey
67
from nacl.signing import SigningKey, VerifyKey
78
from nacl.encoding import Base64Encoder, HexEncoder
@@ -21,6 +22,10 @@
2122

2223
import pyonionreq
2324

25+
if [int(v) for v in nacl.__version__.split('.')] < [1, 4]:
26+
raise ImportError("SOGS requires nacl v1.4.0+")
27+
28+
2429
# generate seed as needed
2530
if not os.path.exists(config.KEY_FILE):
2631
with open(os.open(config.KEY_FILE, os.O_CREAT | os.O_WRONLY, 0o400), 'wb') as f:

sogs/routes/auth.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
from flask import request, abort, Response, g
88
import time
9+
import nacl
910
from nacl.signing import VerifyKey
10-
from nacl.exceptions import BadSignatureError
11+
import nacl.exceptions
1112
import nacl.bindings as salt
1213
import sqlalchemy.exc
1314
from functools import wraps
@@ -206,17 +207,25 @@ def handle_http_auth():
206207
)
207208
blinded_pk = pk[0] == 0x15
208209
pk = pk[1:]
210+
209211
if not salt.crypto_core_ed25519_is_valid_point(pk):
210212
abort_with_reason(
211213
http.BAD_REQUEST,
212-
"Invalid authentication: given X-SOGS-Signature is not a valid Ed25519 pubkey",
214+
"Invalid authentication: given X-SOGS-Pubkey is not a valid Ed25519 pubkey",
213215
)
216+
214217
pk = VerifyKey(pk)
215218
if blinded_pk:
216219
session_id = '15' + pk.encode().hex()
217220
else:
218221
# TODO: if "blinding required" config option is set then reject the request here
219-
session_id = '05' + pk.to_curve25519_public_key().encode().hex()
222+
try:
223+
session_id = '05' + pk.to_curve25519_public_key().encode().hex()
224+
except nacl.exceptions.RuntimeError:
225+
abort_with_reason(
226+
http.BAD_REQUEST,
227+
"Invalid authentication: given X-SOGS-Pubkey is not a valid Ed25519 pubkey",
228+
)
220229

221230
try:
222231
nonce = utils.decode_hex_or_b64(nonce, 16)
@@ -283,7 +292,7 @@ def handle_http_auth():
283292

284293
try:
285294
pk.verify(to_verify, sig_in)
286-
except BadSignatureError:
295+
except nacl.exceptions.BadSignatureError:
287296
abort_with_reason(
288297
http.UNAUTHORIZED, "Invalid authentication: X-SOGS-Signature verification failed"
289298
)

tests/test_auth.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import json
88
from nacl.signing import SigningKey
9+
import nacl.bindings as salt
910

1011

1112
@app.get("/auth_test/whoami")
@@ -492,3 +493,49 @@ def test_auth_legacy(client, db, admin, user, room):
492493
'body': {'status_code': 200, 'banned_members': []},
493494
},
494495
]
496+
497+
498+
def test_small_subgroups(client, db):
499+
# Make some public keys with small subgroup components to make sure sodium rejects them (it
500+
# does, everythwere that matters here).
501+
a = SigningKey.generate()
502+
B = server_pubkey
503+
headers = x_sogs(a, B, 'GET', '/auth_test/whoami')
504+
505+
assert headers['X-SOGS-Pubkey'].startswith('00')
506+
A = bytes.fromhex(headers['X-SOGS-Pubkey'][2:])
507+
508+
assert A == a.verify_key.encode()
509+
510+
if hasattr(salt, 'crypto_core_ed25519_is_valid_point'):
511+
assert salt.crypto_core_ed25519_is_valid_point(A)
512+
513+
Abad = salt.crypto_core_ed25519_add(
514+
A, bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000')
515+
)
516+
517+
if hasattr(salt, 'crypto_core_ed25519_is_valid_point'):
518+
assert not salt.crypto_core_ed25519_is_valid_point(Abad)
519+
520+
headers['X-SOGS-Pubkey'] = '00' + Abad.hex()
521+
522+
r = client.get("/auth_test/whoami", headers=headers)
523+
assert r.status_code == 400
524+
assert r.data == b'Invalid authentication: given X-SOGS-Pubkey is not a valid Ed25519 pubkey'
525+
526+
# Now try with a blinded id:
527+
headers = x_sogs(a, B, 'GET', '/auth_test/whoami', blinded=True)
528+
assert headers['X-SOGS-Pubkey'].startswith('15')
529+
A = bytes.fromhex(headers['X-SOGS-Pubkey'][2:])
530+
531+
Abad = salt.crypto_core_ed25519_add(
532+
A, bytes.fromhex('c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a')
533+
)
534+
535+
if hasattr(salt, 'crypto_core_ed25519_is_valid_point'):
536+
assert not salt.crypto_core_ed25519_is_valid_point(Abad)
537+
538+
headers['X-SOGS-Pubkey'] = '15' + Abad.hex()
539+
r = client.get("/auth_test/whoami", headers=headers)
540+
assert r.status_code == 400
541+
assert r.data == b'Invalid authentication: given X-SOGS-Pubkey is not a valid Ed25519 pubkey'

0 commit comments

Comments
 (0)