Open Table Of Contents

Source code for leekspin.ntor

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Functionality for creating and working with NTOR keys.

For a description of the NTOR handshake protocol, as well as its requisite
keys, see §5.1.4 of `tor-spec.txt`_, as well as the NTOR handshake proposal_.

.. _tor-spec.txt: https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt
.. _proposal: https://gitweb.torproject.org/torspec.git/tree/proposals/216-ntor-handshake.txt
"""

from __future__ import print_function
from __future__ import absolute_import
from __future__ import unicode_literals

import binascii
import logging
import warnings

# Get rid of the `UserWarning: reimporting '_cffi__x6e273d72xa4719796' might
# overwrite older definitions` warnings when importing nacl:
warnings.simplefilter('ignore', UserWarning, append=True)

nacl = None

try:
    import nacl
    import nacl.public
except (ImportError, NameError, IOError) as error:
    logging.warn("Could not import pyNaCl: https://github.com/pyca/pynacl. "\
                 "NTOR key generation will be disabled.")


[docs]class NTORKeyCreationError(Exception): """Raised when there was an error creating an NTOR key."""
[docs]class NTORPublicKeyError(Exception): """Raised when there is an error deriving the public Curve25519 key."""
[docs]def createNTORSecretKey(): """Create a base64-encoded Curve25519 Salsa20-Poly1305 key. See §5.1.4 of `tor-spec.txt`_, as well as torspec.git/proposals/216-ntor-handshake.txt (specifically the "Integrating with the rest of Tor" section towards the end). For a full description of the Curve25519 keypair, see "Curve25519: new Diffie-Hellman speed records" by D.J. Bernstein. .. important:: The subkey used in the NTOR handshake protocol should be generated via HKDF-SHA256 as defined in :rfc:`5869`. .. _tor-spec.txt: https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt :raises: NTORKeyCreationError, if pynacl is not available or not installed, or if there was any other error while creating the key (such as an error due to having a different Python NaCl wrapper installed). :returns: The base64-encoded value of **NTORKey**, if available. The trailing newline of the base64 value is stripped (though not the padding, despite what proposals/216-ntor-handshake.txt says). Otherwise, returns None. """ if nacl is None: raise NTORKeyCreationError("NTOR key creation requires pynacl.") else: try: #: The OR's private NTOR onion key. ntorSK = nacl.public.PrivateKey.generate() except Exception as error: raise NTORKeyCreationError("Error creating NTOR key: %s" % error) else: return ntorSK
[docs]def getNTORPublicKey(ntorSecretKey=None, base64=True): """Get the public key from the secret portion of a Curve25519 keypair. The **base64** version of the public Curve25519 key return from this function is suitable for use in a ``@type [bridge-]server-descriptor``. .. todo:: Remember to tell nickm to fix the description in his proposal about the ntor-onion-key padding removal. :type ntorSecretKey: ``nacl.public.PrivateKey`` :param ntorSecretKey: A key created with :func:`createNTORSecretKey`. If not given, a new one will be created automatically. :param bool base64: If ``True``, return the base64-encoded NTOR public key (with trailing newline removed). :raises: :exc:`NTORPublicKeyError` if there was an error retrieving the public key. :rtype: str or ``nacl.public.PublicKey`` or ``None`` :returns: The base64-encoded string version of the public portion of a Curve25519 keypair, if **base64** is ``True``, otherwise, returns the ``nacl.public.PublicKey``. Returns ``None``, if no **ntorSecretKey** was given and one could not be created. """ if not ntorSecretKey: try: ntorSecretKey = createNTORSecretKey() except NTORKeyCreationError as error: logging.debug(error) if ntorSecretKey: try: #: The OR's public NTOR key; this goes into its #: ``@type [bridge]-server-descriptor``. ntorPublicKey = ntorSecretKey.public_key if base64: ntorPublicKey = binascii.b2a_base64( bytearray(str(ntorPublicKey))).rstrip('\n').rstrip('==') except Exception as error: raise NTORPublicKeyError("Error retrieving the NTOR public key: %s" % error) else: return ntorPublicKey