anonget 1.0 (formerly uinmyn)

StealthMonger StealthMonger at nym.mixmin.net
Sun Sep 18 12:18:42 PDT 2011


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


NEW IN THIS RELEASE

anonget (1.0)

  * Name change from "uinmyn" (last version was 0.07)
  * Optional send confirmation now available, providing a progress
    report of the URL request and to help diagnose failures.
  * Reformat anonget-list-gotten-urls.py output for easier reading.
  * Change "is-not-my.name" to "mixnym.net".
  * Rewrite the documentation.
                                          _     __   ___
     _______ __  ____  _ __  _____ ____ _| |_  /  | / _ \
    /  _  | '_ \/  _ \| '_ \/  _ //  _ \_   _|  | || | | |
    | |_| | | | | |_| | | | | |_| | |/ / | |    | || |_| |
    \___/_|_| |_\____/|_| |_\  __/ \___/ |__|  /___O\___/
                            / /__
			    \____|
           __________________________________________
          |                                          |
          |                                          |
          |   IS THIS ANONYMOUS BROWSING, OR WHAT?!  |
          |                                          |
          |__________________________________________|



N.B.: Below, replace all occurrences of "<AT>" with "@".

WHAT IS IT?

anonget facilitates browsing the Internet with strong anonymity via
url<AT>mixnym.net [1].

HOW IT WORKS

Given a list of URLs, anonget encrypts the list and a fresh random key
and formats it for mailing by mixmaster [2] to url<AT>mixnym.net
through a chain of anonymizing remailers.  The request contains
nothing to identify you or your site, and the outgoing mail is
indistinguishable from other remailer traffic that you may generate.

url<AT>mixnym.net then gets the requested information from the
Internet, encrypts it to the fresh key, and broadcasts it worldwide on
Usenet newsgroup alt.anonymous.messages.

The fresh key (saved by anonget) is used to recognize and decrypt the
reply.

No one, not even the mixnym.net administrator, is able to link content
being fetched with any particular user, or with other requests.

EXAMPLE 1

The command

   echo http://www.banana.mixmin.net | anonget-fetch-body.py db \
    | mixmaster url<AT>mixnym.net

generates a fresh random key for the encryption and hsub of the reply,
adds the key to db (which need not have existed beforehand), formats
and encrypts key and list of URLs (just http://www.banana.mixmin.net
in this case) as required by url<AT>mixnym.net, and sends it.  db is
plain ASCII, so you can look at it to better see what's happening.

Later, in a directory full of fresh a.a.m articles, the command

   for f in *; do anonget-do-aam-article.py db < $f >> mbox; done

will append the requested content to mbox if it has arrived.

ADDING SEND CONFIRMATION

Alternatively, a request can be formulated to not only get the wanted
information as above, but to send confirmation that it was mailed from
the last remailer in the chain.  This provides a progress report of
the request and can help to diagnose failures.  A copy of the request
is sent to a mail-to-news gateway with Subject: another hsub generated
with the same fresh key.

EXAMPLE 2

   (echo 'Chain: *,*,kulin'; echo http://www.banana.mixmin.net \
    | anonget-fetch-whole.py db) | mixmaster

Unlike anonget-fetch-body.py of example 1, anonget-fetch-whole.py
generates a whole mail, including To:, Cc:, and Subject: headers,
instead of relying on mixmaster to provide them.  (Yet more headers,
like the Chain: in example 2, can be added as shown.  Note the
parentheses in the command.)

When anonget-do-aam-article.py processes the article in example 1, it
emits the inner reply mail and deletes the db entry.  But when it
senses that the article is not the returned data, but the request
itself, it constructs a confirmation message and leaves the db entry
intact.  (Note that if the fetched data get processed before the
request confirmation (due to net lag), the request confirmation will
not be noticed because the database entry will already have been
deleted.  But in this case it doesn't matter.)

CACHING OR ARCHIVING THE REPLY, AND RENDERING IT

A convenient way to save and render an anonget reply is to insert it
into the browser-accessible cache provided by wwwoffle [3].  The
script anonget-to-wwwoffle-cache.py is provided for this purpose.

To just list the URLs contained in an "inner mail", the script
anonget-list-gotten-urls.py can be applied.

AUTOMATIC DISPOSITION OF THE REPLY

Instead of the explicit anonget-do-aam-article.py command above,
procmail [4] can be used to forward the reply as local configuration
may require.  Return codes from anonget-do-aam-article.py are useful
for procmail, which might be checking the same message for other hits.
The following .procmailrc clause sequence will send the decrypted
"inner posting" to "local-recipient".

   :0fW
   | anonget-do-aam-article.py /path/to/anonget_pending_requests_db

   :0a
   ! local-recipient

RESPECTING YOUR PRIVACY

Out of respect for your privacy, anonget doesn't require you to access
any web site for its installation or use (though you may if you wish:
mailto:stealthsuite<AT>nym.mixmin.net?subject=send%20index.html).
Everything is right here in this posting.

ABOUT THE CODE

Succinctness and readability are given priority here over efficiency.
If efficiency becomes an issue, there are lots of opportunities here
for speedup.

These scripts are meant mainly as hints for you to adapt to the needs
of your particular case.  But they could be used verbatim if you like.

INSTALLATION

   Have python, gnupg, and mixmaster installed.
   Have public key '94F204C28BF00937EFC85D1AFF4DB66014D0C447' on your
      gnupg keyring.
   Copy the scripts from below to somewhere on your executables path.

Of course, you are taking a full feed of a.a.m at all times without
interruption, separating wheat from chaff only after it's all behind
closed doors.  Otherwise, the world is informed about which articles
you find interesting.

Remember, long random latency is part of the price of anonymity.  It
can't be done with TOR or any other low-latency method.

FOOTNOTES

[1] http://www.mixnym.net
[2] http://sourceforge.net/projects/mixmaster/
[3] WWW OFFLine Explorer http://www.gedanken.demon.co.uk/wwwoffle/
[4] http://www.procmail.org/

CHANGE LOG

   0.07

   Use long keyid, not send<AT>mixnym.net, to identify encryption
   key, foiling fake key postings.  Contributed by Steve Crook.

   Use Python's internal encode('base64') instead of imported Base64
   to encode the random key.  Contributed by Steve Crook.

   Use Python "%"-style string substitution for type conversion and
   tidier code (yet to be finished).  Contributed by Steve Crook.

   Support hsub's ranging from 48 to 80 chars in length.  Contributed
   by Steve Crook.

   Systematically code all email addresses to avert Google corruption
   of its archived version of this posting.

   0.06

   Add time-stamps to database entries to facilitate removal of old,
   stale entries.

   Changed slogan from "Is this anonymous surfing, or what?".

   Documentation improvements.

   0.05

   Quote URL in uinmyn-do-aam-article.py against shell interpretation.

   0.04

   Did TODO item:
   Use PIPE for the gpg stdin in uinmyn-do-aam-article.py.
   (eliminating previous stupid size limit).

   0.03

   Add "decode=True" in uinmyn-to-wwwoffle-cache.py to capture coded
   files, such as .pdfs.

   Change the www.bananasplit.info reference to www.banana.mixmin.net.

   0.02

   Add a script to enter gotten pages into a local browser-accessible
   cache.

   Improvements to the documentation.


Here are the scripts.

8<--------8<------- anonget-fetch-body.py --------8<--------8<--------
#! /usr/bin/python

# SYNOPSIS
#   anonget-fetch-body.py <pending-requests-dict>

# DESCRIPTION

#   Reads URLs from standard input, one per line, generates a fresh
#   random key to encrypt the reply and for its hsub, adds the key and
#   the requested URLs to pending-requests-dict (creating it anew if
#   it doesn't exist), constructs and encrypts a request for
#   url<AT>mixnym.net, and writes it to standard output.

from os import urandom
import sys, os, time
import pprint
from subprocess import Popen, PIPE

ANONGET_PENDING_REQUESTS = sys.argv[1]
NYMSERV_KEY = '94F204C28BF00937EFC85D1AFF4DB66014D0C447'

# same key for both encryption and hsub:
key  = urandom(15).encode('base64').rstrip()

gpgcmd = 'gpg --armor --encrypt --recipient %s ' % NYMSERV_KEY
gpgopts = '--trust-model always --no-emit-version --batch'
gpgproc = Popen(gpgcmd + gpgopts, shell=True, stdin=PIPE)
gpgproc.stdin.write("KEY %s\n" % key)

urls = [time.time()] # time-tag precedes the urls in the db entry
for line in sys.stdin.readlines():
    urls.append(line.rstrip())
    gpgproc.stdin.write('SOURCE %s\n' % line.rstrip())

gpgproc.stdin.close()

# Not dealt with here: Mutually exclusive access to the
# ANONGET_PENDING_REQUESTS database, as would be required if request
# issuance might be concurrent with a.a.m. processing.

if os.path.isfile(ANONGET_PENDING_REQUESTS):
    execfile(ANONGET_PENDING_REQUESTS)
else:
    anonget_pending_requests = {}
anonget_pending_requests[key] = urls

new_upr_fd = open(ANONGET_PENDING_REQUESTS, 'w')
new_upr_fd.write('anonget_pending_requests =\\\n')
pprint.pprint(anonget_pending_requests,stream=new_upr_fd)
8<--------8<------- anonget-do-aam-article.py ----8<--------8<--------
#! /usr/bin/python

# SYNOPSIS
#   anonget-do-aam-article.py <pending-requests-dict>

# DESCRIPTION

#   Reads an article from standard input.  If its Subject: might be an
#   hsub, check it against each key in pending-requests-dict in turn.
#   If a hit is found, decrypt the inner posting and write it to
#   standard output.

import sys, os
from hashlib import sha256
import email
import pprint
from subprocess import Popen, PIPE

ANONGET_PENDING_REQUESTS = sys.argv[1]

posting = email.message_from_file(sys.stdin)

# Abort if Subject doesn't exist, otherwise get its length
if 'Subject' in posting:
    sublen = len(posting['Subject'])
else:
    # No Subject, no hSub.
    sys.exit(1)

# hSubs must fall within the length criteria
if sublen < 48 or sublen > 80:
    sys.exit(1)

try:
    iv = posting['subject'][:16].decode('hex')
except TypeError:
    sys.exit(1)

# Not dealt with here: Mutually exclusive access to the
# ANONGET_PENDING_REQUESTS database, as would be required if request
# issuance might be concurrent with a.a.m. processing.

if os.path.isfile(ANONGET_PENDING_REQUESTS):
    execfile(ANONGET_PENDING_REQUESTS)
else:
    anonget_pending_requests = {}


for key, urls in anonget_pending_requests.iteritems():
    hsub = (iv + sha256(iv + key).digest()).encode('hex')[:sublen]
    if posting['Subject'] == hsub:
        # Doing part of the job in shell:
        gpgproc = Popen('mkfifo $HOME/passwdf ; (echo ' + key + ' > $HOME/passwdf &); gpg --passphrase-fd 3 3<$HOME/passwdf --trust-model always --batch ; rm $HOME/passwdf', stdin=PIPE, stderr=PIPE, shell=True)
        gpgstderr = gpgproc.communicate(input=posting.get_payload())[1]
        if gpgstderr.find('decryption failed') < 0:
            # Other processing that could be done here:
            #  Deal with discrepancies between requested and obtained URL lists.
            #  Save the used key in an archive, rather than losing it forever.
            del anonget_pending_requests[key]
            new_upr_fd = open(ANONGET_PENDING_REQUESTS, 'w')
            new_upr_fd.write('anonget_pending_requests =\\\n')
            pprint.pprint(anonget_pending_requests,stream=new_upr_fd)
            sys.exit(0)
        else:
            # gpg says 'decryption failed'; assume it's a send confirmation.
            print 'Subject: URL request send confirmation ' + key
            print ''
            print 'Key: ' + key
            print 'Request-time: ' + str(urls[0])
            print 'URLs: ' + str(urls[1:])
            print 'gpg-stderr:'
            print gpgstderr
            sys.exit(0)
sys.exit(1)
8<--------8<--------8<------- anonget-fetch-whole.py -------8<--------
#! /usr/bin/python

# SYNOPSIS
#   anonget-fetch-whole.py <pending-requests-dict>

# DESCRIPTION

#   Alternative to anonget-fetch-body.py includes send confirmation.

#   Reads URLs from standard input, one per line, generates a fresh
#   random key to encrypt the reply and for its hsub, adds the key and
#   the requested URLs to pending-requests-dict (creating it anew if
#   it doesn't exist), constructs and encrypts a request for
#   url<AT>mixnym.net, and writes it to standard output, prepended
#   with mail headers for send confirmation.

from os import urandom
from hashlib import sha256
import sys, os, time
import pprint
from subprocess import Popen, PIPE
from datetime import datetime

ANONGET_PENDING_REQUESTS = sys.argv[1]
NYMSERV_KEY = '94F204C28BF00937EFC85D1AFF4DB66014D0C447'
HSUBLEN = 48

# same key for both encryption and hsub:
key  = urandom(15).encode('base64').rstrip()

# Mail headers
print 'To: url<AT>mixnym.net'
datestring = datetime.utcnow().strftime("%Y%m%d")
print 'Cc: mail2news-' + datestring + '-alt.anonymous.messages<AT>m2n.mixmin.net'
iv = urandom(8)                 # hsub code adapted from that of Steve Crook
hsub = (iv + sha256(iv + key).digest()).encode('hex')[:HSUBLEN]
print 'Subject: ' + hsub
print ''
sys.stdout.flush()              # so headers definitely precede body

gpgcmd = 'gpg --armor --encrypt --recipient %s ' % NYMSERV_KEY
gpgopts = '--trust-model always --no-emit-version --batch'
gpgproc = Popen(gpgcmd + gpgopts, shell=True, stdin=PIPE)
gpgproc.stdin.write("KEY %s\n" % key)

urls = [time.time()] # time-tag precedes the urls in the db entry
for line in sys.stdin.readlines():
    urls.append(line.rstrip())
    gpgproc.stdin.write('SOURCE %s\n' % line.rstrip())

gpgproc.stdin.close()

# Not dealt with here: Mutually exclusive access to the
# ANONGET_PENDING_REQUESTS database, as would be required if request
# issuance might be concurrent with a.a.m. processing.

if os.path.isfile(ANONGET_PENDING_REQUESTS):
    execfile(ANONGET_PENDING_REQUESTS)
else:
    anonget_pending_requests = {}
anonget_pending_requests[key] = urls

new_upr_fd = open(ANONGET_PENDING_REQUESTS, 'w')
new_upr_fd.write('anonget_pending_requests =\\\n')
pprint.pprint(anonget_pending_requests,stream=new_upr_fd)

gpgproc.wait()
8<--------8<--------8<------- anonget-list-gotten-urls.py --8<--------
#! /usr/bin/python

# SYNOPSIS
#   anonget-list-gotten-urls.py

# DESCRIPTION

#   List the URLs in the url<AT>mixnym.net "inner mail" on standard
#   input together with the sizes of the fetched pages.

import sys, email

pages = email.message_from_file(sys.stdin)
for page in pages.get_payload():
    print repr(len(page.get_payload())).rjust(8),page.get('Content-Description')
8<--------8<--------8<------- anonget-to-wwwoffle-cache.py -8<--------
#! /usr/bin/python

# SYNOPSIS
#   anonget-to-wwwoffle-cache.py

# DESCRIPTION

#   Given a (decrypted) url<AT>mixnym.net "inner posting", enter each
#   page that it contains into the wwwoffle cache, overlaying any
#   previous content having matching URLs.

import sys, os, email
from subprocess import Popen, PIPE

pages = email.message_from_file(sys.stdin)
for page in pages.get_payload():
    p = Popen('wwwoffle-write \'' + page.get('Content-Description') + '\'', stdin=PIPE, shell=True)
    p.stdin.write('HTTP/1.0 200 OK\n')
    p.stdin.write('Content-Type: ' + page.get('Content-Type') + '\n\n')
    p.stdin.write(page.get_payload(decode=True))
    p.stdin.close()
    p.wait()
8<--------8<--------8<--------8<--------8<--------8<--------8<--------



 -- StealthMonger <StealthMonger<AT>nym.mixmin.net>
    Long, random latency is part of the price of Internet anonymity.

Key: mailto:stealthsuite<AT>nym.mixmin.net?subject=send%20stealthmonger-key

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.8+ <http://mailcrypt.sourceforge.net/>

iEYEARECAAYFAk5cSdcACgkQDkU5rhlDCl4QKwCeL4TMx0mf3zx8Qx647PVfR0cm
Vb0An0Ca73PPHc5qBddPUwG0UrPZPHnq
=H/dc
-----END PGP SIGNATURE-----





More information about the cypherpunks-legacy mailing list