Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,17 +251,31 @@ def banned_user(db):


@pytest.fixture
def blind_user(db):
def blind15_user(db):
import user

return user.User(blinded=True)
return user.User(blinded15=True)


@pytest.fixture
def blind_user2(db):
def blind15_user2(db):
import user

return user.User(blinded=True)
return user.User(blinded15=True)


@pytest.fixture
def blind25_user(db):
import user

return user.User(blinded25=True)


@pytest.fixture
def blind25_user2(db):
import user

return user.User(blinded25=True)


@pytest.fixture
Expand Down
1 change: 0 additions & 1 deletion contrib/auth-example.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def get_signing_headers(
body,
blinded: bool = True,
):

assert len(server_pk) == 32
assert len(nonce) == 16

Expand Down
64 changes: 64 additions & 0 deletions contrib/blind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python3

import sys
import nacl.bindings as sodium
import nacl.hash
import nacl.signing
from nacl.encoding import RawEncoder
from pyonionreq import xed25519

if len(sys.argv) < 3:
print(
f"Usage: {sys.argv[0]} SERVERPUBKEY {{SESSIONID|\"RANDOM\"}} [SESSIONID ...] -- blinds IDs",
file=sys.stderr,
)
sys.exit(1)

server_pk = sys.argv[1]
sids = sys.argv[2:]

if len(server_pk) != 64 or not all(c in '0123456789ABCDEFabcdef' for c in server_pk):
print(f"Invalid argument: expected 64 hex digit server pk as first argument")
sys.exit(2)

server_pk = bytes.fromhex(server_pk)

print(nacl.hash.blake2b(server_pk, digest_size=64, encoder=RawEncoder))

k15 = sodium.crypto_core_ed25519_scalar_reduce(
nacl.hash.blake2b(server_pk, digest_size=64, encoder=RawEncoder)
)


for i in range(len(sids)):
if sids[i] == "RANDOM":
sids[i] = (
"05"
+ nacl.signing.SigningKey.generate()
.verify_key.to_curve25519_public_key()
.encode()
.hex()
)
if (
len(sids[i]) != 66
or not sids[i].startswith('05')
or not all(c in '0123456789ABCDEFabcdef' for c in sids[i])
):
print(f"Invalid session id: expected 66 hex digit id as first argument")

print(f"SOGS pubkey: {server_pk.hex()}")

for s in sids:
s = bytes.fromhex(s)

if s[0] == 0x05:
k25 = sodium.crypto_core_ed25519_scalar_reduce(
nacl.hash.blake2b(s[1:] + server_pk, digest_size=64, encoder=RawEncoder)
)

pk15 = sodium.crypto_scalarmult_ed25519_noclamp(k15, xed25519.pubkey(s[1:]))
pk25 = sodium.crypto_scalarmult_ed25519_noclamp(k25, xed25519.pubkey(s[1:]))

print(
f"{s.hex()} blinds to:\n - 15{pk15.hex()} or …{pk15[31] ^ 0x80:02x}\n - 25{pk25.hex()} or …{pk25[31] ^ 0x80:02x}"
)
111 changes: 111 additions & 0 deletions contrib/blind25-testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env python3

import sys
import nacl.bindings as sodium
import nacl.hash
import nacl.signing
from nacl.encoding import RawEncoder
from pyonionreq import xed25519

server_pk = bytes.fromhex("0000000000000000000000000000000000000000000000000000000000000001")

to_sign = "hello!"

for i in range(1000):
sk = nacl.signing.SigningKey.generate()
pk = sk.verify_key
xpk = pk.to_curve25519_public_key()
sid = "05" + xpk.encode().hex()

k25 = sodium.crypto_core_ed25519_scalar_reduce(
nacl.hash.blake2b(
bytes.fromhex(sid) + server_pk, digest_size=64, encoder=RawEncoder, key=b"SOGS_blind_v2"
)
)

# Comment notation:
# P = server pubkey
# a/A = ed25519 keypair
# b/B = x25519 keypair, converted from a/A
# S = session id = 0x05 || B
# T = |A|, that is, A with the sign bit cleared
# t = private scalar s.t. tG = T (which is ± the private scalar associated with A)
# k = blinding factor = H_64(S || P, key="SOGS_blind_v2")

# This is simulating what the blinding client (i.e. with full keys) can compute:

# k * A
pk25a = sodium.crypto_scalarmult_ed25519_noclamp(k25, pk.encode())
# -k * A
neg_k25 = sodium.crypto_core_ed25519_scalar_negate(k25)
pk25b = sodium.crypto_scalarmult_ed25519_noclamp(neg_k25, pk.encode())

# print(f"k: {k25.hex()}")
# print(f"-k: {neg_k25.hex()}")
#
# print(f"a: {pk25a.hex()}")
# print(f"b: {pk25b.hex()}")

assert pk25a != pk25b
assert pk25a[0:31] == pk25b[0:31]
assert pk25a[31] ^ 0x80 == pk25b[31]

# The one we want to use is what we would end up with *if* our Ed25519 had been positive (but of
# course there's a 50% chance it's negative).
ed_pk_is_positive = pk.encode()[31] & 0x80 == 0

pk25 = pk25a if ed_pk_is_positive else pk25b

###########
# Make sure we can get to pk25 from the session id
# We know sid and server_pk, so we can compute k25
T_pk25 = sodium.crypto_scalarmult_ed25519_noclamp(k25, xed25519.pubkey(xpk.encode()))
assert T_pk25 == pk25

# To sign something that validates with pk25 we have a bit more work

# First get our blinded, private scalar; we'll call it j

# We want to pick j such that it is always associated with |A|, that is, our positive pubkey,
# even if our pubkey is negative, so that someone with our session id can get our signing pubkey
# deterministically.

t = (
sk.to_curve25519_private_key().encode()
) # The value we get here is actually our private scalar, despite the name
if pk.encode()[31] & 0x80:
# If our actual pubkey is negative then negate j so that it is as if we are working from the
# positive version of our pubkey
t = sodium.crypto_core_ed25519_scalar_negate(t)

kt = sodium.crypto_core_ed25519_scalar_mul(k25, t)

kT = sodium.crypto_scalarmult_ed25519_base_noclamp(kt)
assert kT == pk25

# Now we more or less follow EdDSA, but with our blinded scalar instead of real scalar, and with
# a different hash function. (See comments in libsession-util config/groups/keys.cpp for more
# details).
hseed = nacl.hash.blake2b(
sk.encode()[0:31], key=b"SOGS25Seed", encoder=nacl.encoding.RawEncoder
)
r = sodium.crypto_core_ed25519_scalar_reduce(
nacl.hash.blake2b(
hseed + pk25 + to_sign.encode(), 64, key=b"SOGS25Sig", encoder=nacl.encoding.RawEncoder
)
)
R = sodium.crypto_scalarmult_ed25519_base_noclamp(r)

# S = r + H(R || A || M) a (with A=kT, a=kt)
hram = nacl.hash.sha512(R + kT + to_sign.encode(), encoder=nacl.encoding.RawEncoder)
S = sodium.crypto_core_ed25519_scalar_reduce(hram)
S = sodium.crypto_core_ed25519_scalar_mul(S, kt)
S = sodium.crypto_core_ed25519_scalar_add(S, r)

sig = R + S

###########################################
# Test bog standard Ed25519 signature verification:

vk = nacl.signing.VerifyKey(pk25)
vk.verify(to_sign.encode(), sig)
2 changes: 0 additions & 2 deletions contrib/pg-import.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@


with pgsql.transaction():

curin = old.cursor()
curout = pgsql.cursor()

Expand Down Expand Up @@ -131,7 +130,6 @@
curout.execute("ALTER TABLE rooms DROP CONSTRAINT room_image_fk")

def copy(table):

cols = [r['name'] for r in curin.execute(f"PRAGMA table_info({table})")]
if not cols:
raise RuntimeError(f"Expected table {table} does not exist in sqlite db")
Expand Down
16 changes: 9 additions & 7 deletions contrib/upgrade-tests/dump-db.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ def dump_rows(table, extra=None, where=None, order="id", skip=set()):
for r in cur:
table.add_row(
[
'NULL'
if r[i] is None
else int(r[i])
if isinstance(r[i], bool)
else f"{r[i]:.3f}"
if isinstance(r[i], float)
else r[i]
(
'NULL'
if r[i] is None
else (
int(r[i])
if isinstance(r[i], bool)
else f"{r[i]:.3f}" if isinstance(r[i], float) else r[i]
)
)
for i in indices
]
)
Expand Down
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ install_requires=
better_profanity
oxenmq
oxenc
pyonionreq
sqlalchemy
setup_requires=
tomli
2 changes: 1 addition & 1 deletion sogs/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.8.dev0"
__version__ = "0.4.0.dev0"
Loading