Commit 344295d4 authored by putro's avatar putro

first commit

parents
#!/usr/bin/env python
import os
import sys
from utils import download
import subprocess
class MixError(Exception):
pass
def process_args():
""" Process command line arguments """
from optparse import OptionParser
use = "nymhelper.py [options] \nexcept for -C, all other options are ju" \
"st used to send a message from command line\nfor normal use do " \
"not use any options"
parser = OptionParser(usage=use)
parser.add_option("-C", "--config", action="store", dest="config",
default="config.ini",
help="config file to use nym to send the message from.")
parser.add_option("-n", "--nym", action="store", dest="nym", type="int",
help="nym to send the message from.")
parser.add_option("-s", "--subject", action="store", dest="subject",
help="subject for the message to be sent.")
parser.add_option("-r", "--recipient", action="store", dest="recipient",
help="recipient of the message to be sent.")
parser.add_option("-t", "--text", action="store", dest="text",
help="text of the message to be sent.")
parser.add_option("-c", "--chain", action="store", dest="chain",
help="remailer chain to send the message through \
(example: -c \"remailer1,remailer2\").")
return parser.parse_args()
def checkAscii(file):
""" Check if the a file is pure ascii """
import codecs
bad = 0
f = codecs.open(file, encoding='ascii')
lines = open(file).readlines()
for i in range(0, len(lines)):
try:
l = f.readline()
except:
num = i + 1
print "config file problem (%s): line %d contains non-ascii " \
"character, fix it" % (file, num)
bad = 1
break
f.close()
return bad
def InitialCheck():
global configfile
global opts, args
(opts, args) = process_args()
if opts.config:
configfile = os.path.dirname(sys.argv[0]) + "/" + opts.config
else:
configfile = os.path.dirname(sys.argv[0]) + "/config.ini"
def OptionsCheck(config):
""" performs some checks """
try:
f = open(os.path.dirname(sys.argv[0]) + "/check", "w")
except:
print "Error: you cannot write %s, fix permission or launch this script from another directory" % options['path'] + "/" + options['stats-file']
sys.exit()
else:
f.close()
os.remove(os.path.dirname(sys.argv[0]) + "/check")
# try:
# f = open(os.path.dirname(sys.argv[0]) + "/" + config['stats']['stats_mlist'], "r")
# except:
# download(config['stats']['stats_mlist'], config)
# else:
# f.close()
try:
download(config['stats']['stats_mlist'], config)
except:
print "Error: cannot download fresh remailer stats"
try:
mix = subprocess.Popen(config['options']['mixmaster'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError: # usally means that mixmaster could not be executed
raise MixError('Could not find mixmaster binary.')
#import gpgfuncts
#gpgfuncts.selectSecKey(options.nym_email)
return True
import os
import sys
import checks
from configobj import ConfigObj, ConfigObjError
class ConfigError(Exception):
pass
configfile = os.path.dirname(sys.argv[0]) + "/config.ini"
try:
config = ConfigObj(configfile, file_error=True)
except (ConfigObjError, IOError), e:
print 'Could not read "%s": %s' % (configfile, e)
sys.exit(-1)
nyms = config['nym']
def writeConfig():
config.write()
def checkSection(section, config=config):
if section in listSection(config):
return True
def listSection(config):
return config.keys()
def configSection(nym, subject, subj_type, passphrase=""):
config['nym'][nym] = {}
config['nym'][nym]['subject'] = subject
config['nym'][nym]['subj_type'] = subj_type
config['nym'][nym]['passphrase'] = passphrase
def checkNymConfig(nym):
return nyms.has_key(nym)
def nymList(nyms):
nymlist = []
for n in nyms.keys():
nymlist.append(n)
return nymlist
def printNymList():
nymlist = nymList()
counter = 0
for n in nymlist:
print counter, "-", n
counter += 1
return counter
This diff is collapsed.
Copyright (c) 2008-2013 by Vinay Sajip.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name(s) of the copyright holder(s) may not be used to endorse or
promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This diff is collapsed.
import sys
import re
import gnupg
import getpass
from config import config
from utils import pressKey
try:
global gpg
gpg = gnupg.GPG()
except:
print "Error, problems with your keyring directory, exiting"
sys.exit(0)
def createKey(email, name, comment="", key_type="RSA", key_length=4096):
""" Create a gpg key """
while True:
pwd = getpass.getpass("insert the password: ")
pwd2 = getpass.getpass("type again the password: ")
if pwd == pwd2:
break
print "wait, I'm generating the key, move the mouse, press keys, it could take some times......\n"
input_data = gpg.gen_key_input(name_real=name, name_email=email, name_comment="", passphrase=pwd, key_type=key_type, key_length=key_length, )
key = gpg.gen_key(input_data)
def selectKey(email, secret=False):
""" Select the key fingerprint of an email address"""
keys = gpg.list_keys(secret=secret)
key = False
for k in keys:
counter = 0
# if keys have multiple uids, scan through all uids
for u in k["uids"]:
# stop at first match
e = k["uids"][counter]
counter += 1
expr = re.compile("%s" % email)
m = expr.search(e)
""" if match found, select key """
if m:
"""check if key is valid"""
if encrypt("test", k["fingerprint"], test=True).status == "encryption ok":
key = True
config['last_message'] = "OK - key choosed: keyID=%s" % getKeyID(k["fingerprint"])
break
else:
config['last_message'] = "ERROR - invalid key for recipient: %s" % u
key = False
if key:
break
if not key:
config['last_message'] = "ERROR, public key for %s NOT found" % email
print "ERROR, public key for %s NOT found" % email
pressKey()
return False
return k["fingerprint"]
def listSecKeys():
""" Print list of secret keys in keyring"""
seckeys = gpg.list_keys(secret=True)
counter = 0
for k in seckeys:
counter += 1
print "%s - %s - %s" % (counter, k["uids"][0], k["keyid"])
print [x for x in k["uids"]]
return seckeys
def selectSecKey(email):
""" Select the key fingerprint of an email address"""
global last_message
seckeys = gpg.list_keys(True)
key = False
for k in seckeys:
counter = 0
"""if keys have multiple uids, scan through all uids"""
for u in k["uids"]:
"""stop at first match"""
e = k["uids"][counter]
counter += 1
expr = re.compile("%s" % email)
m = expr.search(e)
""" if match found, select key """
if m:
#print "key fingerprint for %s: %s" % (email, k["fingerprint"])
config['nym_fp'] = k["fingerprint"]
config['nym_keyid'] = getKeyID(k["fingerprint"])
key = True
last_message = "OK - secret key choosed: keyID=%s" % config['nym_keyid']
break
if key:
break
if not key:
config['last_message'] = "ERROR, secret key for %s NOT found" % email
return False
return k["fingerprint"]
def getKeyID(fp):
""" Get keyid from fingeprint """
# works only with v4 keys
return fp[-8:]
def exportPubKey(id):
""" Export public key
id can be the fingerprint """
ascii_armored_public_keys = stripVersion(str(gpg.export_keys(id)))
return ascii_armored_public_keys
def defineSecKey():
""" Select which secret key to use """
ls = listSecKeys()
numkeys = int(len(ls))
print "there are %s keys" % numkeys
if numkeys == 0:
print "going to generate a new key..."
createKey()
listSecKeys()
while True:
a = raw_input("enter key number (and press enter): ")
if a.isdigit():
a = int(a)
if a < 1:
print "you can't enter a key number lower than 1"
continue
elif a > numkeys:
print "there are only %d keys, select key number again" % numkeys
continue
break
a -= 1
return ls[a]
def deleteKey(fp):
""" Delete secret and public key """
global last_message
seckey = gpg.list_keys(secret=True)
str(gpg.delete_keys(fp, True))
str(gpg.delete_keys(fp))
last_message = "OK - key with fingerprint %s deleted" % fp
def stripVersion(pgptext):
"""Version strings in PGP blocks can weaken anonymity by partitioning users
into subsets. This function replaces the Version string with 'N/A'."""
newtext = re.sub('(?m)^Version: .*', 'Version: N/A', pgptext)
return newtext
def encrypt(message, recipient, sign=False, passphrase=False, test=False):
""" Encrypt a message for a recipient """
encrypted_ascii_data = gpg.encrypt(message, recipient, always_trust=True,
sign=sign, passphrase=passphrase)
if not test:
if encrypted_ascii_data.status != "encryption ok":
config['last_message'] = "ERROR " + encrypted_ascii_data.status + " (recipient: %s)" % recipient
return False
else:
return stripVersion(str(encrypted_ascii_data))
else:
return encrypted_ascii_data
#!/usr/bin/env python
import os
import sys
import checks
from config import config, nyms, nymList, printNymList, configSection, writeConfig
import traceback
import re
from stats import parse_stats, stats_m, uptime_sort_m, format_stats
from utils import pressKey, validateChoice, chooseList, askPassphrase, askSomething, askYesNo
from utils import chooseSubjType, selectServer, getDomain, validateEmail, isGpg
import gpgfuncts
import easygui as eg
import string
import subprocess
class bcolors:
"""
Define colors for outputs
"""
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
ENDC = '\033[0m'
def disable(self):
self.GREEN = ''
self.YELLOW = ''
self.RED = ''
self.ENDC = ''
def menu():
""" Print menu and wait for user choice """
os.system(['clear', 'cls'][os.name == 'nt'])
print bcolors.GREEN + "----- NYM HELPER -----" + bcolors.ENDC
if anym is False:
print bcolors.RED + "active nym: NONE"
else:
print bcolors.GREEN + "active nym: %s " % anym.name
if re.match("OK", config['last_message']):
print bcolors.YELLOW + "\ndebug: %s\n" % config['last_message'] + bcolors.ENDC
else:
print bcolors.RED + "\ndebug: %s\n" % config['last_message'] + bcolors.ENDC
#present menu and get selection
selection = raw_input("Enter\n\
1 to choose/select active nym\n\
2 to write a message (as active nym)\n\
3 to send the message created via local mixmaster\n\
4 to download fresh remailer stats and keys\n\
5 to choose remailers chain\n\
6 to create or update a nym\n\
8 to create a new secret key\n\
9 to delete a secret key\n\
10 to send msg via smtp (NOT ANONYMOUS)\n\
q to quit\n\n\n")
#perform actions based on selection above
if selection == "1":
askNym()
menu()
elif selection == "2":
writeMessage()
menu()
elif selection == "3":
sendMixMsg()
menu()
elif selection == "4":
download(options.stats_rlist)
download(options.stats_mlist)
download(options.keys_file, pgp=True)
menu()
elif selection == "5":
sendMixMsg(dest="drugo@autistici.org", msg="test")
menu()
elif selection == "6":
createNym()
menu()
elif selection == "7":
modifyConfig()
menu()
elif selection == "8":
print "\nsecret keys in your keyring:"
gpgfuncts.listSecKeys()
print "\n"
gpgfuncts.createKey()
print "key generated, secret keys in your keyring: "
gpgfuncts.listSecKeys()
pressKey()
menu()
elif selection == "9":
sk = gpgfuncts.defineSecKey()
fp = sk["fingerprint"]
deleteKey(fp)
msg = "key of %s deleted" % sk["uids"][1]
config['last_message'] = msg
menu()
elif selection == "10":
sendMsg()
menu()
elif selection == "q":
#f = open(options.message_out, "w")
# shred messages_out ??
sys.exit("bye bye")
else:
print "wrong selection"
pressKey()
menu()
def askNym():
""" set active nym """
nymlist = nymList(nyms)
setActiveNym(chooseList(nymlist))
return
def createNym():
name = askSomething('Enter the name of the nym (without domain): ')
server = selectServer(config)
email = name + "@" + server
if not gpgfuncts.selectKey(email):
print "no key availabke, creating a new one for %s" % email
gpgfuncts.createKey(email, name)
subj_type = chooseSubjType(config)
subject = askSomething('Enter the subject you want to use with this nym: ')
configSection(email, subject, subj_type)
writeConfig()
setActiveNym(email)
msg = prepareSetupMsg(gpgfuncts.selectSecKey(email), subj_type, subject)
dest = "config@" + server
emsg = signCryptMsg(msg, dest, sign=False)
if not emsg:
config['last_message'] = "Something went wrong"
menu()
sendMixMsg(dest, emsg)
#return
def prepareSetupMsg(keyid, subj_type, subject):
key = gpgfuncts.exportPubKey(keyid)
msg = "%s = %s\n\n" % (subj_type, subject) + key
return msg
def signCryptMsg(msg, dest, sign):
msg = gpgfuncts.encrypt(msg, dest, sign=sign)
return msg
def setActiveNym(nym):
global anym
anym = config['nym'][nym]
anym.domain = getDomain(anym.name)
anym.fp = gpgfuncts.selectSecKey(anym.name)
class MixError(Exception):
pass
def sendMixMsg(dest, msg, subj="subject", test=False):
""" send msg through a local mixmaster client """
args = [config['options']['mixmaster'], '--mail', '--to=' + dest, '--subject=' + subj, '--copies=1']
print "\nChoose remailer chain used to send the message to %s\n" % dest
chooseChain()
args.append('--chain=' + ','.join(config['remailer_chain']))
try:
mix = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = mix.communicate(msg)
if err.find('Error') >= 0:
raise MixError('Mixmaster process returned the following error: ' + str(err) + '. Sending failed.')
config['last_message'] = "OK, succesfully sent email"
except OSError: # usally means that mixmaster could not be executed
raise MixError('Could not find mixmaster binary.')
def writeMessage():
"""
Create a message to be sent from nym
The message has to be sent through a remailer chain
to send@nym-server-address
"""
if anym.fp is False:
config['last_message'] = "ERROR - select your nym, actual selection HAS NOT a secret key"
menu()
subject = raw_input("Insert message subject: ")
recipient = raw_input("Insert recipient email address or newsgroup name: ")
text = eg.textbox(msg='Write your message....', title='Input Box', text='', codebox=0)
text = filter(lambda x: x in string.printable, text)
if isGpg(text):
print "message already encrypted"
pressKey()
elif gpgfuncts.encrypt("test", recipient, test=True).status == "encryption ok":
# barbatrucco veramente infimo per risolvere il problema per cui
# la prima riga del messaggio viene scartata
text = "Message:\n" + text
text = gpgfuncts.encrypt(text, recipient)
print "message has been encrypted with %s public key" % recipient
pressKey()
else:
print "msg has not been encrypted, public key for %s not found" % recipient
pressKey()
msg = "From: %s\n" % anym.name
if validateEmail(recipient):
msg = msg + "To: %s\n" % recipient
else:
msg = msg + "To: %s\n" % options['mail-to-news']
msg = msg + "Newsgroups: %s\n" % recipient
msg = msg + "Subject: %s\n\n" % subject
if not anym['passphrase']:
pwd = askPassphrase()
else:
pwd = anym['passphrase']
msg = msg + text
dest = 'send@' + anym.domain
emsg = gpgfuncts.stripVersion(str(gpgfuncts.encrypt(msg, dest,
sign=anym.fp,
passphrase=pwd)))
sendMixMsg(dest, emsg)
def modifyConfig():
pass
checks.InitialCheck()
if checks.OptionsCheck(config) == True:
config['last_message'] = "OK, initial check passed"
if not nyms.sections:
print "No nyms configured, going to create it..."
createNym()
else:
setActiveNym(nyms.sections[0])
def chooseChain():
""" Print remailer list and prompt for chain choice """
for remailer in uptime_sort_m():
print format_stats(remailer)
chain = raw_input('Choose remailer to be chained (by num), separated by commas, or write "r" for a random chain (3 remailers): ')
if chain == "r":
#chooseRandomChain()
config['remailer_chain'] = ['*','*','*']
else:
validateChain(chain)
#def chooseRandomChain():
# """ Choose a 3 random remailer chain """
# import random
# remailers = uptime_sort_m()
# remailers[:] = [r for r in remailers if stats_m[r].uptime > float(config['stats']['remailer_min_uptime'])]
# n = len(remailers)
#
# r1 = str(random.randrange(1, n + 1))
# r2 = str(random.randrange(1, n + 1))
# while r2 == r1:
# r2 = str(random.randrange(1, n + 1))
# r3 = str(random.randrange(1, n + 1))
# while r3 == r2:
# r3 = str(random.randrange(1, n + 1))
# while True:
# if checkMiddle(remailers[int(r3) - 1]):
# r3 = str(random.randrange(1, n + 1))
# else:
# break
# chain = r1 + "," + r2 + "," + r3
# validateChain(chain, random=True)
def validateChain(chain):
"""
Validate remailer chain, check the availability of public key of remailers
and that last remailer of chain is not middleman
"""
remailer_chain = []
comma = re.compile(',')