Web-based micropayment scheme

Will Ware wware at world.std.com
Wed Aug 1 06:19:06 CEST 2001


#!/usr/bin/python
"""Probabilistic micropayment scheme

   I am not a big-time web jock. Big-time web jocks will laugh
   at my simplistic use of CGI and my obvious ignorance of web
   security issues. This is a proof of concept. Live with it.

Because of transaction processing costs, it's impractical to ask
people to pay a few cents or a fractional cent for downloadable
content. If it could be made practical, it could open up new vistas of
web commerce. This is a very crude prototype of a scheme to do that,
using probabilistic payments. Suppose that I agree that some piece of
content is worth a nickel. I'm not willing to use a 34-cent stamp to
send a nickel to the creator of the content, nor are our banks willing
to process a check for five cents, but I might be amenable to a fair
lottery that gives me one chance in 100 of owing five dollars.

Assuming the customer agrees in principle, the next problem is to
arrange a lottery that both vendor and customer agree is fair. Let N
be the multiplier (the 100 in the example above) and P be the amount
of the check (five bucks, above) so that the agreed-upon value of the
content is P/N. Here's my idea for a fair lottery protocol.

1. Customer and vendor agree to terms (including values of P and N).
2. Vendor generates a random 128-bit number R1 and computes its
   MD5 hash H1. Vendor transmits H1 and content to customer.
3. Customer generates a random 128-bit number R2 and transmits R2
   to vendor.
4. Vendor transmits R1 to customer. Customer confirms that MD5 hash
   of R1 is really H1.
5. Customer and vendor both compute ((R1+R2) % N). If this number is
   zero, then customer owes amount P to vendor. Otherwise customer
   gets the content for free.

The guarantee of fairness is that neither vendor nor customer can
manipulate the quantity ((R1+R2)%N), because the vendor doesn't know
R2 when he generates R1, and the customer doesn't know R1 when he
generates R2. The hash value H1 represents the vendor's commitment to
the value of R1: it assures the customer that the vendor did not
change R1 after learning the value of R2.

One nice thing here is that no third party is required. Anybody could
implement this protocol on their webserver and start selling content,
as long as their customers are smart enough to understand how the
protocol works.

This script implements both ends of the transaction, on a Red Hat 6.2
Linux box, with the server running Apache. Because of an awful kludge
at one point, you need to maintain a directory at
/home/httpd/cgi-bin/data/ which should be writeable/readable by anybody.
Put a copy of this script in /home/httpd/cgi-bin and chmod it to 755.
Then visit http://localhost/cgi-bin/micropay.py to try out the protocol.
"""

import os, md5, random, types

def str2long(s):
    Y = 0L
    for x in map(ord, s):
        Y = (Y << 8) + x
    return Y

def long2str(x):
    s = ""
    for i in range(16):
        y, x = chr(x & 0xFF), x >> 8
        s = y + s
    return s

def random128():
    # There aren't really 128 bits of entropy here, there are actually
    # only 24 bits because of the way random.random() works. This is a
    # bug, because it admits the possibility that the customer could
    # exhaustively search all possible initial states for random.random()
    # to reverse-hash H1 and figure out R1. But this is only a prototype.
    X = 0L
    for i in range(8):
        X = (X << 16) + long(0x10000 * random.random())
    return X

def hash(x):
    s = long2str(x)
    s = md5.md5(s).digest()
    return str2long(s)

N = 100

if os.environ.has_key("HTTP_HOST"):

    # Server-side CGI script

    import cgi, string
    form = cgi.FieldStorage()
    info = { }
    gotAny = 0
    for k in form.keys():
        try:
            info[k] = string.atol(form[k].value)
        except:
            try:
                # Try stripping the trailing "L"
                info[k] = string.atol(form[k].value[:-1])
            except:
                info[k] = form[k].value
        gotAny = 1

    if not info.has_key("R2"):

        # First pass
        # Generate R1, compute H1, give H1 to customer
        R1 = random128()
        H1 = hash(R1)
        # I need to pass R1 and H1 to the second pass instance of this
        # script. How the hell do I do that? I guess I'll just write
        # R1 to a file on the server. That obviously wouldn't work in
        # a real system where many people might be talking to the
        # server at a time, but it'll do for now. Maybe it could work
        # with MySQL if I tagged incoming requests in some repeatable
        # unique way, but I'm not enough of a web jock to know how.
        outf = open("data/r1crud", "w")
        outf.write(repr(R1))
        outf.close()

        print "Content-type: text/html\n"
        print "<HTML><H1>Probabilistic micropayment protocol</H1>"
        print "First pass<p>"
        print "H1 is ", repr(H1), "<p>"
        print """
        <FORM METHOD=GET ACTION="http://localhost/cgi-bin/micropay.py">
        Enter R2: <INPUT TYPE=TEXT NAME=R2 VALUE="" SIZE=50>
        <INPUT TYPE=SUBMIT VALUE="OK">
        <INPUT TYPE=RESET VALUE="Reset">
        </FORM>
        """

    else:

        # Second pass
        # Retrieve R1 from the file I wrote before.
        inf = open("data/r1crud")
        R1 = string.atol(inf.read()[:-1])
        inf.close()
        H1 = hash(R1)
        R2 = info["R2"]
        assert type(R2) == types.LongType
        print "Content-type: text/html\n"
        print "<HTML><H1>Probabilistic micropayment protocol</H1>"
        print "Second pass<p>"
        print "R1 is ", repr(R1), "<p>"
        print "H1 is ", repr(H1), "<p>"
        print "R2 is ", repr(R2), "<p>"
        msum = (R1 + R2) % N
        print "(R1 + R2) % N is", msum, "<p>"
        if msum == 0L:
            print "You owe me a check. I have recorded that fact in my"
            print "database, and will be expecting it in the mail soon."
        else:
            print "You do not owe me a check. Enjoy your content."
        print "</HTML>"

else:

    # Client-side script

    print "Get H1 from vendor, enter here (including trailing 'L'):"
    H1 = input()
    assert type(H1) == types.LongType
    R2 = random128()
    print "\nHere is R2, pass it to vendor:"
    print R2
    print "\nGet R1 from vendor, enter here (including trailing 'L'):"
    R1 = input()
    assert type(R1) == types.LongType
    if hash(R1) != H1:
        print "\nH1 is not the correct hash for R1. The vendor may be"
        print "trying to rip you off."
        raise "Ouch, that hurts"

    msum = (R1 + R2) % N
    print '\n(R1 + R2) % N =', msum
    if msum == 0L:
        print "You owe the vendor a check. Sorry."
    else:
        print "You do not owe the vendor a check this time. Rejoice."

# end of script

-- 
-----------------------------------+---------------------
 22nd century: Esperanto, geodesic | Will Ware
 domes, hovercrafts, metric system | wware at world.std.com



More information about the Python-list mailing list