[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Simplify HSv3 blinding
commit 838f6209babae74279dec100df13c3f594425a8f
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Nov 17 14:24:51 2019 -0800
Simplify HSv3 blinding
I won't pretend to understand this math. A smarter mind than mine (asn's) came
up with this crypto. Just massaging it into a form I find easier to understand.
---
stem/descriptor/certificate.py | 6 +--
stem/descriptor/hidden_service.py | 73 ++++++++++++++++++++++++-------
stem/descriptor/hsv3_crypto.py | 66 ----------------------------
stem/{descriptor => util}/slow_ed25519.py | 8 ++--
test/settings.cfg | 2 +
5 files changed, 64 insertions(+), 91 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py
index 74a4e08c..fe94b52d 100644
--- a/stem/descriptor/certificate.py
+++ b/stem/descriptor/certificate.py
@@ -291,15 +291,13 @@ class Ed25519CertificateV1(Ed25519Certificate):
:var bytes signature: certificate signature
:param bytes signature: pre-calculated certificate signature
- :param cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey: certificate signing key
+ :param cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey signing_key: certificate signing key
"""
def __init__(self, cert_type = None, expiration = None, key_type = None, key = None, extensions = None, signature = None, signing_key = None):
super(Ed25519CertificateV1, self).__init__(1)
- if not signature and not signing_key:
- raise ValueError('Certificate signature or signing key is required')
- elif cert_type is None:
+ if cert_type is None:
raise ValueError('Certificate type is required')
elif key is None:
raise ValueError('Certificate key is required')
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index cdb8d645..3af2fa0c 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -21,6 +21,8 @@ These are only available through the Controller's
BaseHiddenServiceDescriptor - Common parent for hidden service descriptors
|- HiddenServiceDescriptorV2 - Version 2 hidden service descriptor
+- HiddenServiceDescriptorV3 - Version 3 hidden service descriptor
+ |- address_from_identity_key - convert an identity key to address
+ |- identity_key_from_address - convert an address to identity key
+- decrypt - decrypt and parse encrypted layers
OuterLayer - First encrypted layer of a hidden service v3 descriptor
@@ -41,7 +43,6 @@ import time
import stem.client.datatype
import stem.descriptor.certificate
-import stem.descriptor.hsv3_crypto
import stem.prereq
import stem.util
import stem.util.connection
@@ -50,6 +51,7 @@ import stem.util.tor_tools
from stem.client.datatype import CertType
from stem.descriptor.certificate import ExtensionType, Ed25519Extension, Ed25519Certificate, Ed25519CertificateV1
+from stem.util import slow_ed25519
from stem.descriptor import (
PGP_BLOCK_END,
@@ -903,7 +905,7 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
}
@classmethod
- def content(cls, attr = None, exclude = (), sign = False, inner_layer = None, outer_layer = None, identity_key = None, signing_key = None, signing_cert = None, revision_counter = None, blinding_param = None):
+ def content(cls, attr = None, exclude = (), sign = False, inner_layer = None, outer_layer = None, identity_key = None, signing_key = None, signing_cert = None, revision_counter = None, blinding_nonce = None):
"""
Hidden service v3 descriptors consist of three parts:
@@ -933,7 +935,7 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
:param stem.descriptor.Ed25519CertificateV1 signing_cert: certificate
signing this descriptor
:param int revision_counter: descriptor revision number
- :param bytes blinding_param: 32 byte blinding factor to derive the blinding key
+ :param bytes blinding_nonce: 32 byte blinding factor to derive the blinding key
:returns: **str** with the content of a descriptor
@@ -953,10 +955,10 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
identity_key = identity_key if identity_key else Ed25519PrivateKey.generate()
signing_key = signing_key if signing_key else Ed25519PrivateKey.generate()
revision_counter = revision_counter if revision_counter else int(time.time())
- blinding_param = blinding_param if blinding_param else os.urandom(32)
+ blinding_nonce = blinding_nonce if blinding_nonce else os.urandom(32)
- blinded_key = stem.descriptor.hsv3_crypto.HSv3PrivateBlindedKey(identity_key, blinding_param = blinding_param)
- subcredential = HiddenServiceDescriptorV3._subcredential(identity_key, blinded_key.blinded_pubkey)
+ blinded_key = _blinded_pubkey(identity_key, blinding_nonce)
+ subcredential = HiddenServiceDescriptorV3._subcredential(identity_key, blinded_key)
custom_sig = attr.pop('signature') if (attr and 'signature' in attr) else None
if not outer_layer:
@@ -965,23 +967,21 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
inner_layer = inner_layer,
revision_counter = revision_counter,
subcredential = subcredential,
- blinded_key = blinded_key.blinded_pubkey,
+ blinded_key = blinded_key,
)
if not signing_cert:
- signing_cert = Ed25519CertificateV1(
- cert_type = CertType.HS_V3_DESC_SIGNING,
- key = signing_key,
- extensions = [Ed25519Extension(ExtensionType.HAS_SIGNING_KEY, None, blinded_key.blinded_pubkey)],
- signing_key = blinded_key,
- )
+ extensions = [Ed25519Extension(ExtensionType.HAS_SIGNING_KEY, None, blinded_key)]
+
+ signing_cert = Ed25519CertificateV1(cert_type = CertType.HS_V3_DESC_SIGNING, key = signing_key, extensions = extensions)
+ signing_cert.signature = _blinded_sign(signing_cert.pack(), identity_key, blinded_key, blinding_nonce)
desc_content = _descriptor_content(attr, exclude, (
('hs-descriptor', '3'),
('descriptor-lifetime', '180'),
('descriptor-signing-key-cert', '\n' + signing_cert.to_base64(pem = True)),
('revision-counter', str(revision_counter)),
- ('superencrypted', b'\n' + outer_layer._encrypt(revision_counter, subcredential, blinded_key.blinded_pubkey)),
+ ('superencrypted', b'\n' + outer_layer._encrypt(revision_counter, subcredential, blinded_key)),
), ()) + b'\n'
if custom_sig:
@@ -993,8 +993,8 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
return desc_content
@classmethod
- def create(cls, attr = None, exclude = (), validate = True, sign = False, inner_layer = None, outer_layer = None, identity_key = None, signing_key = None, signing_cert = None, revision_counter = None, blinding_param = None):
- return cls(cls.content(attr, exclude, sign, inner_layer, outer_layer, identity_key, signing_key, signing_cert, revision_counter, blinding_param), validate = validate)
+ def create(cls, attr = None, exclude = (), validate = True, sign = False, inner_layer = None, outer_layer = None, identity_key = None, signing_key = None, signing_cert = None, revision_counter = None, blinding_nonce = None):
+ return cls(cls.content(attr, exclude, sign, inner_layer, outer_layer, identity_key, signing_key, signing_cert, revision_counter, blinding_nonce), validate = validate)
def __init__(self, raw_contents, validate = False):
super(HiddenServiceDescriptorV3, self).__init__(raw_contents, lazy_load = not validate)
@@ -1294,6 +1294,47 @@ class InnerLayer(Descriptor):
self._entries = entries
+def _blinded_pubkey(identity_key, blinding_nonce):
+ mult = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(blinding_nonce, i) for i in range(3, slow_ed25519.b - 2))
+ P = slow_ed25519.decodepoint(stem.util._pubkey_bytes(identity_key))
+ return slow_ed25519.encodepoint(slow_ed25519.scalarmult(P, mult))
+
+
+def _blinded_sign(msg, identity_key, blinded_key, blinding_nonce):
+ from cryptography.hazmat.primitives import serialization
+
+ identity_key_bytes = identity_key.private_bytes(
+ encoding = serialization.Encoding.Raw,
+ format = serialization.PrivateFormat.Raw,
+ encryption_algorithm = serialization.NoEncryption(),
+ )
+
+ # pad private identity key into an ESK (encrypted secret key)
+
+ h = slow_ed25519.H(identity_key_bytes)
+ a = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(h, i) for i in range(3, slow_ed25519.b - 2))
+ k = b''.join([h[i:i + 1] for i in range(slow_ed25519.b // 8, slow_ed25519.b // 4)])
+ esk = slow_ed25519.encodeint(a) + k
+
+ # blind the ESK with this nonce
+
+ mult = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(blinding_nonce, i) for i in range(3, slow_ed25519.b - 2))
+ s = slow_ed25519.decodeint(esk[:32])
+ s_prime = (s * mult) % slow_ed25519.l
+ k = esk[32:]
+ k_prime = slow_ed25519.H(b'Derive temporary signing key hash input' + k)[:32]
+ blinded_esk = slow_ed25519.encodeint(s_prime) + k_prime
+
+ # finally, sign the message
+
+ a = slow_ed25519.decodeint(blinded_esk[:32])
+ r = slow_ed25519.Hint(b''.join([blinded_esk[i:i + 1] for i in range(slow_ed25519.b // 8, slow_ed25519.b // 4)]) + msg)
+ R = slow_ed25519.scalarmult(slow_ed25519.B, r)
+ S = (r + slow_ed25519.Hint(slow_ed25519.encodepoint(R) + blinded_key + msg) * a) % slow_ed25519.l
+
+ return slow_ed25519.encodepoint(R) + slow_ed25519.encodeint(S)
+
+
# TODO: drop this alias in stem 2.x
HiddenServiceDescriptor = HiddenServiceDescriptorV2
diff --git a/stem/descriptor/hsv3_crypto.py b/stem/descriptor/hsv3_crypto.py
deleted file mode 100644
index 0186ba90..00000000
--- a/stem/descriptor/hsv3_crypto.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from stem.descriptor import slow_ed25519
-
-
-"""
-HSv3 Key blinding
-
-Expose classes for HSv3 blinded keys which can mimic the hazmat ed25519
-public/private key classes, so that we can use them interchangeably in the
-certificate module.
-
-- HSv3PublicBlindedKey: represents the public blinded ed25519 key of an onion
- service and should expose a public_bytes() method and a verify() method.
-
-- HSv3PrivateBlindedKey: represents the private part of a blinded ed25519 key
- of an onion service and should expose a public_key() method and a sign() method.
-"""
-
-
-def blindESK(esk, param):
- mult = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(param, i) for i in range(3, slow_ed25519.b - 2))
- s = slow_ed25519.decodeint(esk[:32])
- s_prime = (s * mult) % slow_ed25519.l
- k = esk[32:]
- assert(len(k) == 32)
- k_prime = slow_ed25519.H(b'Derive temporary signing key hash input' + k)[:32]
- return slow_ed25519.encodeint(s_prime) + k_prime
-
-
-def blindPK(pk, param):
- mult = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(param, i) for i in range(3, slow_ed25519.b - 2))
- P = slow_ed25519.decodepoint(pk)
- return slow_ed25519.encodepoint(slow_ed25519.scalarmult(P, mult))
-
-
-def expandSK(sk):
- h = slow_ed25519.H(sk)
- a = 2 ** (slow_ed25519.b - 2) + sum(2 ** i * slow_ed25519.bit(h, i) for i in range(3, slow_ed25519.b - 2))
- k = b''.join([h[i:i + 1] for i in range(slow_ed25519.b // 8, slow_ed25519.b // 4)])
- assert len(k) == 32
- return slow_ed25519.encodeint(a) + k
-
-
-def signatureWithESK(m, h, pk):
- a = slow_ed25519.decodeint(h[:32])
- r = slow_ed25519.Hint(b''.join([h[i:i + 1] for i in range(slow_ed25519.b // 8, slow_ed25519.b // 4)]) + m)
- R = slow_ed25519.scalarmult(slow_ed25519.B, r)
- S = (r + slow_ed25519.Hint(slow_ed25519.encodepoint(R) + pk + m) * a) % slow_ed25519.l
-
- return slow_ed25519.encodepoint(R) + slow_ed25519.encodeint(S)
-
-
-class HSv3PrivateBlindedKey(object):
- def __init__(self, hazmat_private_key, blinding_param):
- from cryptography.hazmat.primitives import serialization
-
- secret_seed = hazmat_private_key.private_bytes(encoding = serialization.Encoding.Raw, format = serialization.PrivateFormat.Raw, encryption_algorithm = serialization.NoEncryption())
- assert(len(secret_seed) == 32)
-
- expanded_identity_priv_key = expandSK(secret_seed)
- identity_public_key = slow_ed25519.publickey(secret_seed)
-
- self.blinded_secret_key = blindESK(expanded_identity_priv_key, blinding_param)
- self.blinded_pubkey = blindPK(identity_public_key, blinding_param)
-
- def sign(self, msg):
- return signatureWithESK(msg, self.blinded_secret_key, self.blinded_pubkey)
diff --git a/stem/descriptor/slow_ed25519.py b/stem/util/slow_ed25519.py
similarity index 94%
rename from stem/descriptor/slow_ed25519.py
rename to stem/util/slow_ed25519.py
index ffca5b02..fb617464 100644
--- a/stem/descriptor/slow_ed25519.py
+++ b/stem/util/slow_ed25519.py
@@ -1,12 +1,10 @@
-# This is the ed25519 implementation from
-# http://ed25519.cr.yp.to/python/ed25519.py .
-# It is in the public domain.
+# Public domain ed25519 implementation from...
+#
+# http://ed25519.cr.yp.to/python/ed25519.py
#
# It isn't constant-time. Don't use it except for testing. Also, see
# warnings about how very slow it is. Only use this for generating
# test vectors, I'd suggest.
-#
-# Don't edit this file. Mess with ed25519_ref.py
import hashlib
diff --git a/test/settings.cfg b/test/settings.cfg
index 8fe79d2a..5443df41 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -177,6 +177,8 @@ pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.m
pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.networkstatus
pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.server_descriptor
pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.tordnsel
+pycodestyle.ignore stem/util/slow_ed25519.py => E741: l = 2 ** 252 + 27742317777372353535851937790883648493
+pycodestyle.ignore stem/util/slow_ed25519.py => E741: I = expmod(2, (q - 1) // 4, q)
pycodestyle.ignore test/unit/util/connection.py => W291: _tor tor 15843 10 pipe 0x0 state:
pycodestyle.ignore test/unit/util/connection.py => W291: _tor tor 15843 11 pipe 0x0 state:
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits