« Playtime: ProLiant DL385, Fedora Core 5, Python, VS2005 R2 | Main | Upcoming OSI Album Free »

April 5, 2006

RSA-704 "Lottery" Script

Since I found out that RSA-640 was solved, I've been thinking about altering my script for RSA-704. I made a number of changes:

  • Focusing on generating binary numbers with exactly half the digits of RSA-704. Both RSA-640 and RSA-576's factors had that property.
  • Used join() to concatenate the binary number string elements instead of +='s.
  • Used Python's native base-10 conversion instead of baseconvert.
  • Decided to calculate the primes below 2000 and check these against the generated number, since that might save compute cycles versus trying every number against RSA-704. It seems like it makes things go faster.
  • Improved the output.

Here it is: download text version

#!/usr/bin/env python

# rsa-704-rand.py:
# RSA-704 Factoring Challenge "Lottery Solution" by Alex Harden
# Version 1.0: 05-Apr-2006

# Copyright (C) 2003,2006  Alex Harden (http://alexharden.org/blog/)

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

# Find the GPL online at: http://www.gnu.org/copyleft/gpl.html

# Info about the RSA Challenge Numbers:
# http://www.rsasecurity.com/rsalabs/challenges/factoring/numbers.html

# Interesting fact about the solutions to the RSA-576 and RSA-640
# challenges: both factors were 50% of the number of digits of the
# challenge number's total digits. (Both binary and decimal.)

# RSA-576 numbers: for reference and testing:
# x=18819881292060796383869723946165043980716356337941738270076335642298885971\
# 5234665485319060606504743045317388011303396716199692321205734031879550656996\
# 221305168759307650257059L
# y=39807508642406493739712550055038649119906436234252670840638518957594638895\
# 7261768583317L
# z=47277214610743530253622307197304822463291469530209711645985217113052071125\
# 6363590397527L

# RSA-640 numbers:
# x=31074182404900437213507500358885679300373460228427275457201619488232064405\
# 1808150455634682967172328678243791627283803341547107310850191954852900733772\
# 4822783525742386454014691736602477652346609L
# y=16347336458092538484431338838650908598417836700330923121811108523893331001\
# 04508151212118167511579L
# z=19008712816648221131268515739354139754718967899685154936666385390880271038\
# 02104498957191261465571L

# For RSA-640 (193 digits), I concentrated on generating random odd numbers
# that had 96-98 digits (est. 319-321 binary digits).  The solution numbers 
# each had 97 digits and were each 320 binary digits (the middle of the
# estimated range).

# For RSA-704 (212 decimal digits), I'm concentrating on generating 
# random odd numbers that have 352 binary digits. (106 decimal digits)

# Let's try to make likely prime numbers using a neat method:

# Reference 1: http://www.maths.abdn.ac.uk/~igc/tch/mx3015/notes/node157.html
# - create random bitstring of 352 binary digits (make first and last
#   values "1")
# - convert it to decimal long int
# - if it's not divisible by any of the primes < 2000, see if it's a factor
#   of RSA-704  (this step is optional - not sure how expensive the "z=x%n"
#   actually is)

# Reference 2: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/162479
########################################

from random import seed, random

outfile='./rsa-704.txt'  #if a factor is found, write it here
i=0
digits=352
N=2000
primes=[p for p in range(2,N) if 0 not in [p%d for d in range(2,p)]]
z=1
seed()
x=74037563479561712828046796097429573142593188889231289084936232638972765034028266276891996419625117843995894330502127585370118968098286733173273108930900552505116877063299072396380786710086096962537934650563796359L
while z <> 0 :
    i+=1
    slist=['1']
    pos=1
    while pos < digits-1 :
        if round(random(),0) :
            slist+='1'
        else :
            slist+='0'
        pos+=1
    slist+='1'
    h="".join(slist)
    n=long(h,2)
    checkme=1
    for p in primes :
        if n%p == 0 :
            checkme=0
            break
    if checkme :
        z=x%n
        if i%500 == 0 :
            print 'i=',i
            print 'n=',n
            print 'z=',z
            print '----------------'
        if z == 0:
            print 'found it'
            print 'factor1=',n
            print 'factor2=',x/n
            print 'challenge=',x
            file=open(outfile,'a+')
            file.write(str(n))
            file.write('\n')
            file.close()

Comments

Pretty cool; I remember this from the last time you posted it.

I've got a major speedup for you, if you're interested. The above code does about 40K tries per second on my 2GhZ; I was able to get it up to 1 million tries per second by substituting the following code: delete the following:


slist=['1']
pos=1
while pos

and replace it with the following two lines:


n=getrandbits(digits);
n |= (1

Use the power of Python, and stop all those silly slow string manipulations! :)

Cool! I'll probably post a 1.1 with that change (and any other cleaning up I can think of). I did try to research stuff that I could speed up from the RSA-640 script.

I also have a simpler variation that uses an implementation of Rabin-Miller algorithms I found on ASPN to try to generate large prime numbers, which I then try against RSA-704. I'll post that soon.

Oh yeah, that's a lot faster, though I didn't understand what your second statement was for. I didn't include it when substituting getrandbits() in for the string cat that I was doing. Guess I should have looked at all of random.py's functions!

The second statement was just to ensure that the random number was the right length, by making sure the first binary digit was a 1.

Otherwise, if I generate n random bits, and the first is zero, then I really have a number that is n-1 (or less) digits, because of the leading zero(s).

See, still have my matress, don't I?

I'll expect a 2% royalty when you win the prize. :)

Cool. Thanks for the explanation; that makes sense. Obviously, my original implementation's initialization of slist as '1' took care of that case. But your code runs faster. ;)

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)