Opened 9 months ago

Last modified 9 months ago

#29429 new defect

Tor 0.3.5.7 may be generating v2 RSA keys that are unparsable by STEM/PyCrypto?

Reported by: alecmuffett Owned by:
Priority: Medium Milestone: Tor: unspecified
Component: Core Tor/Tor Version: Tor: 0.3.5.7
Severity: Normal Keywords: rsa key format not supported
Cc: Actual Points:
Parent ID: Points:
Reviewer: Sponsor:

Description

I am developing EOTK - https://blog.torproject.org/volunteer-spotlight-alec-helps-companies-activate-onion-services - this means a lot of work with OnionBalance and STEM, and a lot of disposable Onion addresses; so I am going to break a cardinal rule and intentionally paste some private keys into this bug report. It's okay, they are trash anyway.

This evening I have found behaviour that I have not seen before, and I would like to report it.

I have built Tor from source:

Feb 07 21:35:37.251 [notice] Tor 0.3.5.7 running on Linux with Libevent 2.0.21-stable, OpenSSL 1.0.2q, Zlib 1.2.8, Liblzma N/A, and Libzstd N/A.

I am running it on Raspbian Stretch, latest-everything, compilers, etc; happy to give specific details upon request.

I have generated 12 onion addresses by means of launching Tor with a configuration file that looks like this:

DataDirectory $dir/
Log info file $dir/tor.log
PidFile $dir/tor.pid
RunAsDaemon 1
SocksPort 0
HiddenServiceDir $dir
HiddenServicePort 1 127.0.0.1:1
HiddenServiceVersion 2

...running it as "tor -f $dir/config", killing the daemon after a few seconds, and then scraping $dir for the onion private key for deployment / reuse. Relevant source code at: https://github.com/alecmuffett/eotk/blob/master/lib.d/generate-onion-key.sh

This gives me the 12 onion addresses that I need, and normally this would work, however OnionBalance is not accepting some of the addresses; I cut the OnionBalance python code down to this simple test:

#!/usr/bin/env python
import sys
import Crypto
from Crypto.PublicKey import RSA
for fname in sys.argv[1:]:
    with open (fname, 'rt') as fh:
        pem = fh.read() # slurp
        try:
            rsa = Crypto.PublicKey.RSA.importKey(pem, passphrase=None)
        except Exception as e:
            print fname, e
        else:
            print fname, rsa.size()

...which I then run against my 12 keys:

$ ./pemtest.py  secrets.d/*.key
secrets.d/3rxg2cbaolntggg6.key 1023
secrets.d/gno6yqj4uik34nbk.key 1023
secrets.d/gpozevhubaeuimoy.key RSA key format is not supported
secrets.d/l4gymyc6r3zbkafu.key 1023
secrets.d/mrukxceh7grqzmbr.key 1023
secrets.d/ntz22knrkak4od7q.key RSA key format is not supported
secrets.d/pjclxtjzo7g7wiwd.key 1023
secrets.d/qjgqctbh2rkm3ov2.key 1023
secrets.d/sr4xt3tiz4lietmo.key RSA key format is not supported
secrets.d/udtev77zeo6x7bli.key RSA key format is not supported
secrets.d/y62hgkk2ztzhkfzz.key 1023
secrets.d/zdsv5364zgb6m7l6.key RSA key format is not supported

...and the problem is fairly evident; to take this one as an example:

$ cat secrets.d/ntz22knrkak4od7q.key
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDsCx3zjT0R13eQkUuhJgb6pG5z2zF+6baeJrc3JSCstQh/8tR4
5yI3Gwo7c1sRGA4Yjy8RrOKhMg3uXd+NyekySGlxpK4evKq22g2+TX73yOpJ5yVH
h655rZEUwoYq1M3HBL4nhIwSBt5js2/pGVyvMT2aZeMDo45NQ7Bb/t3/AQIDAQAB
AoGAMUgk6bu8W2REJ1/ejXe2D1CTawcBr4C2SxDEQfQzfTuS2bvmVpPTVfQET+NG
ySvfjYsfha415vffZrwct6rHT/yed7KBN3l4DeF4PfQNBfBNHUj08Z3WQBwuhTZQ
Sh15Oc2iuZ2ZwEzH7bjP7sMz6FW3hQ10MY/Fe7zGAOd4Is0CQQD2zJp/7jr9ySnI
ciu7VXO1wsxKKmrswTod+V/R0teTTZemDKNtEtX9ol61MOfoAjXhyRRmk3JgkXtA
r2XdvOXvAkEA9Nfcy1bWR+E1LmFg6S4GarG95a4fvQlQNEKvJLjHw25GZ1iRKdbb
orP0qiw6enA0PhOwKy373kFvzTNVQfHaDwJBAITLHIqfZbBuUAQhonQ/C26ObRuu
7S+M3LeKGcutlf8VbfaTsE+dJfU+K5V0xiNpJRLi/g4fYhihzt7EQZxo6pMCQAtQ
8sZ/I/Y8hW24WHdOhkNmJaW474SYKpnPvzKOS8VPkndyU3tAj/QsJxG6a5V/HBsG
Y+0K+goish0k0zryB6cCQQDo/u8TeKKiJbH2I8PJNtja6+yRcS2IashnqMLQYHBS
4W1lcmZcXxyj7Re7jexM7W83s3XG6rpLoLNzmmUoFyZI
-----END RSA PRIVATE KEY-----

I ran it through a online validator which appeared happy, so I am wondering if there is some edge-case recently arrived in Tor v2 Key Generation, which is breaking PyCrypto and hence OnionBalance and/or other STEM-dependent code?

The full set of keys (good and bad) is available on request.

Child Tickets

Change History (5)

comment:1 Changed 9 months ago by alecmuffett

A quick check through my archive of ~ 100 private keys, generated on my Mac running Mojave with Tor via Homebrew, shows 3x keys generated on the Mac, on January 21st, which show the same issue. That was also 0.3.5.7

comment:2 Changed 9 months ago by alecmuffett

Traceback from another example:

yk4dvcfi6uwk5j2s.key RSA key format is not supported
Traceback (most recent call last):
  File "/Users/alecm/scripts/pemtest.py", line 10, in <module>
    rsa = Crypto.PublicKey.RSA.importKey(pem, passphrase=None)
  File "build/bdist.macosx-10.14-intel/egg/Crypto/PublicKey/RSA.py", line 665, in importKey
    return self._importKeyDER(der)
  File "build/bdist.macosx-10.14-intel/egg/Crypto/PublicKey/RSA.py", line 588, in _importKeyDER
    raise ValueError("RSA key format is not supported")
ValueError: RSA key format is not supported

comment:3 Changed 9 months ago by alecmuffett

Summary: Tor 0.3.5.7 on latest Raspbian may be generating v2 RSA keys that are unparsable by STEM/PyCrypto?Tor 0.3.5.7 may be generating v2 RSA keys that are unparsable by STEM/PyCrypto?

Amended title because it's not just Raspbian.

comment:4 Changed 9 months ago by yawning

secrets.d/ntz22knrkak4od7q.key appears to be well-formed:

SEQUENCE (9 elem)
  INTEGER 0
  INTEGER (1024 bit) 165755330484047200219364772901691969775438019984010204690806665657623…
  INTEGER 65537
  INTEGER (1022 bit) 346068661521463881563325783544234741052736382111053546971070937972940…
  INTEGER (512 bit) 1292592463411186436245179016401295264821905996683157528634376452165948…
  INTEGER (512 bit) 1282347957117237125403480981340955515808793868467580464551866348900888…
  INTEGER (512 bit) 6954954916652984924746898340686768531274680128940626505978915243713907…
  INTEGER (508 bit) 5926777182519967732497892629326570116704818083812657318961202329708092…
  INTEGER (512 bit) 1220298207425842139178939640359368043909653140162843027417635854509182…

lib/Crypto/PublicKey/RSA.py (fresh checkout from github):

    def _importKeyDER(self, extern_key, passphrase=None):
        """Import an RSA key (public or private half), encoded in DER form."""

        try:

            der = decode_der(DerSequence, extern_key)

            # Try PKCS#1 first, for a private key
            if len(der) == 9 and der.hasOnlyInts() and der[0] == 0:
                # ASN.1 RSAPrivateKey element
                del der[6:]     # Remove d mod (p-1),
                                # d mod (q-1), and
                                # q^{-1} mod p
                der.append(inverse(der[4], der[5]))  # Add p^{-1} mod q
                del der[0]      # Remove version
                return self.construct(der[:])

            # Public key import elided for brevity.

        except (ValueError, EOFError):
            pass

        raise ValueError("RSA key format is not supported")

The de-serialized DER is indeed a sequence of 9 elements, only containing INTEGERs, with the version being 0, so it's probably failing to decode what appears to be well formed DER. It could be puking in the ctor as well, but apart from asserting that it's passed longs, there doesn't look like there's much else going on there.

Last edited 9 months ago by yawning (previous) (diff)

comment:5 Changed 9 months ago by nickm

The only thing I can think that we changed in 0.3.5 is that we changed from using OpenSSL's implementation of PEM to doing the base64-encoding ourselves, but that doesn't seem to be the issue here.

Note: See TracTickets for help on using tickets.