Esempio n. 1
0
def generateCertChain(filename, mmtpKey, identityKey, nickname,
                      certStarts, certEnds):
    """Create a two-certificate chain for use in MMTP.

       filename -- location to store certificate chain.
       mmtpKey -- a short-term RSA key to use for connection
           encryption (1024 bits).
       identityKey -- our long-term signing key (2048-4096 bits).
       nickname -- nickname to use in our certificates.
       certStarts, certEnds -- certificate lifetimes.
    """
    fname = filename+"_tmp"
    mixminion.Crypto.generate_cert(fname,
                                   mmtpKey, identityKey,
                                   "%s<MMTP>" %nickname,
                                   nickname,
                                   certStarts, certEnds)
    certText = readFile(fname)
    os.unlink(fname)
    mixminion.Crypto.generate_cert(fname,
                                   identityKey, identityKey,
                                   nickname, nickname,
                                   certStarts, certEnds)

    identityCertText = readFile(fname)
    os.unlink(fname)
    writeFile(filename, certText+identityCertText, 0600)
Esempio n. 2
0
    def __init__(self, location, purpose, klen, vlen, vdflt):
        """Create a new JournaledDBBase that stores its files to match the
           pattern 'location*', whose journal-encoded keys are all of length
           klen, whose journal-encoded values are all of length vlen."""
        DBBase.__init__(self, location, purpose)

        self.klen = klen
        self.vlen = vlen
        self.vdefault = vdflt

        self.journalFileName = location+"_jrnl"
        self.journal = {}
        # If there's a journal file, snarf it into memory.
        if os.path.exists(self.journalFileName):
            j = readFile(self.journalFileName, 1)
            for i in xrange(0, len(j), klen+vlen):
                if vlen:
                    self.journal[j[i:i+klen]] = j[i+klen:i+klen+vlen]
                else:
                    self.journal[j[i:i+klen]] = self.vdefault

        self.journalFile = os.open(self.journalFileName,
                                   _JOURNAL_OPEN_FLAGS|os.O_APPEND, 0600)

        self.sync()
Esempio n. 3
0
    def __init__(self, location, purpose, klen, vlen, vdflt):
        """Create a new JournaledDBBase that stores its files to match the
           pattern 'location*', whose journal-encoded keys are all of length
           klen, whose journal-encoded values are all of length vlen."""
        DBBase.__init__(self, location, purpose)

        self.klen = klen
        self.vlen = vlen
        self.vdefault = vdflt

        self.journalFileName = location + "_jrnl"
        self.journal = {}
        # If there's a journal file, snarf it into memory.
        if os.path.exists(self.journalFileName):
            j = readFile(self.journalFileName, 1)
            for i in xrange(0, len(j), klen + vlen):
                if vlen:
                    self.journal[j[i:i + klen]] = j[i + klen:i + klen + vlen]
                else:
                    self.journal[j[i:i + klen]] = self.vdefault

        self.journalFile = os.open(self.journalFileName,
                                   _JOURNAL_OPEN_FLAGS | os.O_APPEND, 0600)

        self.sync()
Esempio n. 4
0
 def messageContents(self, handle):
     """Given a message handle, returns the contents of the corresponding
        message."""
     try:
         self._lock.acquire()
         return readFile(os.path.join(self.dir, "msg_"+handle), 1)
     finally:
         self._lock.release()
Esempio n. 5
0
 def messageContents(self, handle):
     """Given a message handle, returns the contents of the corresponding
        message."""
     try:
         self._lock.acquire()
         return readFile(os.path.join(self.dir, "msg_" + handle), 1)
     finally:
         self._lock.release()
Esempio n. 6
0
def generateCertChain(filename, mmtpKey, identityKey, nickname, certStarts,
                      certEnds):
    """Create a two-certificate chain for use in MMTP.

       filename -- location to store certificate chain.
       mmtpKey -- a short-term RSA key to use for connection
           encryption (1024 bits).
       identityKey -- our long-term signing key (2048-4096 bits).
       nickname -- nickname to use in our certificates.
       certStarts, certEnds -- certificate lifetimes.
    """
    fname = filename + "_tmp"
    mixminion.Crypto.generate_cert(fname, mmtpKey, identityKey,
                                   "%s<MMTP>" % nickname, nickname, certStarts,
                                   certEnds)
    certText = readFile(fname)
    os.unlink(fname)
    mixminion.Crypto.generate_cert(fname, identityKey, identityKey, nickname,
                                   nickname, certStarts, certEnds)

    identityCertText = readFile(fname)
    os.unlink(fname)
    writeFile(filename, certText + identityCertText, 0600)
Esempio n. 7
0
    def getPingerSeed(self):
        """DOCDOC"""
        if self.pingerSeed is not None:
            return self.pingerSeed

        fn = os.path.join(self.keyDir, "pinger.seed")
        if os.path.exists(fn):
            checkPrivateFile(fn)
            r = readFile(fn)
            if len(r) == mixminion.Crypto.DIGEST_LEN:
                self.pingerSeed = r
                return r

        self.pingerSeed = r = mixminion.Crypto.trng(mixminion.Crypto.DIGEST_LEN)
        createPrivateDir(self.keyDir)
        writeFile(fn, r, 0600)
        return r
Esempio n. 8
0
def _readEncryptedFile(fname, password, magicList):
    """Read encrypted data from the file named 'fname', using the password
       'password' and checking for a magic string contained in 'magicList'.
       Returns the magic string and the plaintext file contents on success.

       If the file is corrupt or the password is wrong, raises BadPassword.
       If the magic is incorrect, raises ValueError.
    """
    assert list(map(len, magicList)) == [8]*len(magicList)

    text = readFile(fname)
    r = unarmorText(text, ["TYPE III KEYRING"])
    if len(r) != 1:
        raise ValueError("Bad ascii armor on keyring")
    tp, headers, s = r[0]
    assert tp == "TYPE III KEYRING"
    vers = [ v for k,v in headers if k == 'Version' ]
    if not vers or vers[0] != '0.1':
        raise ValueError("Unrecognized version on keyring")

    if len(s) < MAGIC_LEN+1 or s[MAGIC_LEN] != '\x00':
        raise ValueError("Unrecognized encryption format on %s"%fname)
    if s[:MAGIC_LEN] not in magicList:
        raise ValueError("Invalid versioning on %s"%fname)
    magic = s[:8]
    s = s[MAGIC_LEN+1:]
    if len(s) < 28:
        raise MixError("File %s is too short."%fname)
    salt = s[:SALT_LEN]
    s = s[SALT_LEN:]
    key = sha1(salt+password+salt)[:AES_KEY_LEN]
    s = ctr_crypt(s, key)
    data = s[:-DIGEST_LEN]
    digest = s[-DIGEST_LEN:]
    if digest != sha1(data+salt+magic):
        raise BadPassword()

    # We've decrypted it; now let's extract the data from the padding.
    if len(data) < 4:
        raise MixError("File %s is too short"%fname)
    length, = struct.unpack("!L", data[:4])
    if len(data) < length+4:
        raise MixError("File %s is too short"%fname)

    return magic, data[4:4+length]
Esempio n. 9
0
def _readEncryptedFile(fname, password, magicList):
    """Read encrypted data from the file named 'fname', using the password
       'password' and checking for a magic string contained in 'magicList'.
       Returns the magic string and the plaintext file contents on success.

       If the file is corrupt or the password is wrong, raises BadPassword.
       If the magic is incorrect, raises ValueError.
    """
    assert list(map(len, magicList)) == [8] * len(magicList)

    text = readFile(fname)
    r = unarmorText(text, ["TYPE III KEYRING"])
    if len(r) != 1:
        raise ValueError("Bad ascii armor on keyring")
    tp, headers, s = r[0]
    assert tp == "TYPE III KEYRING"
    vers = [v for k, v in headers if k == 'Version']
    if not vers or vers[0] != '0.1':
        raise ValueError("Unrecognized version on keyring")

    if len(s) < MAGIC_LEN + 1 or s[MAGIC_LEN] != '\x00':
        raise ValueError("Unrecognized encryption format on %s" % fname)
    if s[:MAGIC_LEN] not in magicList:
        raise ValueError("Invalid versioning on %s" % fname)
    magic = s[:8]
    s = s[MAGIC_LEN + 1:]
    if len(s) < 28:
        raise MixError("File %s is too short." % fname)
    salt = s[:SALT_LEN]
    s = s[SALT_LEN:]
    key = sha1(salt + password + salt)[:AES_KEY_LEN]
    s = ctr_crypt(s, key)
    data = s[:-DIGEST_LEN]
    digest = s[-DIGEST_LEN:]
    if digest != sha1(data + salt + magic):
        raise BadPassword()

    # We've decrypted it; now let's extract the data from the padding.
    if len(data) < 4:
        raise MixError("File %s is too short" % fname)
    length, = struct.unpack("!L", data[:4])
    if len(data) < length + 4:
        raise MixError("File %s is too short" % fname)

    return magic, data[4:4 + length]
Esempio n. 10
0
    def getPingerSeed(self):
        """DOCDOC"""
        if self.pingerSeed is not None:
            return self.pingerSeed

        fn = os.path.join(self.keyDir, "pinger.seed")
        if os.path.exists(fn):
            checkPrivateFile(fn)
            r = readFile(fn)
            if len(r) == mixminion.Crypto.DIGEST_LEN:
                self.pingerSeed = r
                return r

        self.pingerSeed = r = mixminion.Crypto.trng(
            mixminion.Crypto.DIGEST_LEN)
        createPrivateDir(self.keyDir)
        writeFile(fn, r, 0600)
        return r
Esempio n. 11
0
    def publish(self, url):
        """Try to publish this descriptor to a given directory URL.  Returns
           'accept' if the publication was successful, 'reject' if the
           server refused to accept the descriptor, and 'error' if
           publication failed for some other reason."""
        fname = self.getDescriptorFileName()
        descriptor = readFile(fname)
        fields = urllib.urlencode({"desc" : descriptor})
        f = None
        try:
            try:
                if sys.version_info >= (2,7,9):
                    ctx = ssl.create_default_context()
                    ctx.check_hostname = False
                    ctx.verify_mode = ssl.CERT_NONE
                    infile = urllib2.urlopen(request, context=ctx)
                else:
                    infile = urllib2.urlopen(request)

                #############################################
                # some python versions verify certificates
                # anemone.mooo.com uses a self-signed cert
                # this workaround is not a problem because
                # the directory information is already signed
                # (although as Zax says, it is certainly a
                # kludge ;)
                if sys.version_info >= (2,7,9):
                    ctx = ssl.create_default_context()
                    ctx.check_hostname = False
                    ctx.verify_mode = ssl.CERT_NONE
                    f = urllib2.urlopen(url, fields, context=ctx)
                else:
                    f = urllib2.urlopen(url, fields, context=ctx)
                #############################################
                #f = urllib2.urlopen(url, fields)
                info = f.info()
                reply = f.read()
            except IOError, e:
                log.error("Error while publishing server descriptor: %s",e)
                return 'error'
            except:
                log.exception("Error publishing server descriptor")
                return 'error'
Esempio n. 12
0
 def publish(self, url):
     """Try to publish this descriptor to a given directory URL.  Returns
        'accept' if the publication was successful, 'reject' if the
        server refused to accept the descriptor, and 'error' if
        publication failed for some other reason."""
     fname = self.getDescriptorFileName()
     descriptor = readFile(fname)
     fields = urllib.urlencode({"desc": descriptor})
     f = None
     try:
         try:
             f = urllib2.urlopen(url, fields)
             info = f.info()
             reply = f.read()
         except IOError, e:
             LOG.error("Error while publishing server descriptor: %s", e)
             return 'error'
         except:
             LOG.error_exc(sys.exc_info(),
                           "Error publishing server descriptor")
             return 'error'
Esempio n. 13
0
 def publish(self, url):
     """Try to publish this descriptor to a given directory URL.  Returns
        'accept' if the publication was successful, 'reject' if the
        server refused to accept the descriptor, and 'error' if
        publication failed for some other reason."""
     fname = self.getDescriptorFileName()
     descriptor = readFile(fname)
     fields = urllib.urlencode({"desc" : descriptor})
     f = None
     try:
         try:
             f = urllib2.urlopen(url, fields)
             info = f.info()
             reply = f.read()
         except IOError, e:
             LOG.error("Error while publishing server descriptor: %s",e)
             return 'error'
         except:
             LOG.error_exc(sys.exc_info(),
                           "Error publishing server descriptor")
             return 'error'
Esempio n. 14
0
    def generateDirectory(self,
                          startAt, endAt, extraTime,
                          identityKey,
                          publicationTime=None,
                          badServers=(),
                          excludeServers=()):
        """Generate and sign a new directory, to be effective from <startAt>
           through <endAt>.  It includes all servers that are valid at
           any time between <startAt> and <endAt>+<extraTime>.  The directory
           is signed with <identityKey>.

           Any servers whose nicknames appear in 'badServers' are marked as
           not recommended; any servers whose nicknames appear in
           'excludeServers' are left off the directory entirely.
        """
        try:
            self._lock()
            self.clean()
            if publicationTime is None:
                publicationTime = time.time()
            if previousMidnight(startAt) >= previousMidnight(endAt):
                raise MixError("Validity range does not contain a full day.")

            excludeServers = [ nickname.lower() for nickname in excludeServers]

            # First, sort all servers by nickname.
            includedByNickname =  {}
            for fn, s in self.servers.items():
                nickname = s.getNickname().lower()
                if nickname in excludeServers: continue
                includedByNickname.setdefault(nickname, []).append((s, fn))

            # Second, find all servers that are valid for part of the period,
            # and that aren't superseded for the whole period.
            timeRange = IntervalSet([(previousMidnight(startAt),
                                      endAt+extraTime)])

            for nickname, ss in includedByNickname.items():
                # We prefer the most-recently-published descriptor.  If two
                # are published at the same time, we prefer the one that
                # expires last.
                ss = [ (s['Server']['Published'],
                        s['Server']['Valid-Until'],
                        s, fn) for s,fn in ss]
                ss.sort()
                ss.reverse()
                uncovered = timeRange.copy()
                included = []
                for _, _, s, fn in ss:
                    valid = s.getIntervalSet()
                    if (uncovered * valid):
                        included.append((s, fn))
                        uncovered -= valid
                includedByNickname[nickname] = included

            # Now sort the remaining servers by nickname, then by valid-after.
            included = []
            for ss in includedByNickname.values():
                for s,fn in ss:
                    nickname = s.getNickname()
                    validAfter = s['Server']['Valid-After']
                    included.append((nickname, validAfter, fn))
            included.sort()

            # FFFF We should probably not do all of this in RAM, but
            # FFFF what the hey.  It will only matter if we have many, many
            # FFFF servers in the system.
            contents = [ ]
            for _, _, fn in included:
                txt = readFile(os.path.join(self.serverDir, fn))
                contents.append(txt)

            goodServers = [n for n,_,_ in included if n not in badServers]
            g = {}
            for n in goodServers: g[n]=1
            goodServers = g.keys()
            goodServers.sort()
            goodServers = ", ".join(goodServers)

            clientVersions = self.config['Directory']['ClientVersions']
            serverVersions = self.config['Directory']['ServerVersions']

            #FFFF Support for multiple signatures
            header = """\
            [Directory]
            Version: 0.2
            Published: %s
            Valid-After: %s
            Valid-Until: %s
            Recommended-Servers: %s
            [Signature]
            DirectoryIdentity: %s
            DirectoryDigest:
            DirectorySignature:
            [Recommended-Software]
            MixminionClient: %s
            MixminionServer: %s
            """ % (formatTime(publicationTime),
                   formatDate(startAt),
                   formatDate(endAt),
                   goodServers,
                   formatBase64(pk_encode_public_key(identityKey)),
                   ", ".join(clientVersions),
                   ", ".join(serverVersions))

            directory = header+"".join(contents)
            directory = _getDirectoryDigestImpl(directory, identityKey)

            # Make sure that the directory checks out
            # FFFF remove this once we are _very_ confident.
            if 1:
                parsed = ServerDirectory(string=directory)
                includedDigests = {}
                for _, _, fn in included:
                    includedDigests[self.servers[fn]['Server']['Digest']] = 1
                foundDigests = {}
                for s in parsed.getAllServers():
                    foundDigests[s['Server']['Digest']] = 1
                assert foundDigests == includedDigests

            writeFile(os.path.join(self.baseDir, "directory"),
                      directory,
                      mode=0644)

            f, _ = openUnique(os.path.join(self.dirArchiveDir,
                                            "dir-"+formatFnameTime()))
            f.write(directory)
            f.close()
        finally:
            self._unlock()
Esempio n. 15
0
    def generateDirectory(self,
                          startAt,
                          endAt,
                          extraTime,
                          identityKey,
                          publicationTime=None,
                          badServers=(),
                          excludeServers=()):
        """Generate and sign a new directory, to be effective from <startAt>
           through <endAt>.  It includes all servers that are valid at
           any time between <startAt> and <endAt>+<extraTime>.  The directory
           is signed with <identityKey>.

           Any servers whose nicknames appear in 'badServers' are marked as
           not recommended; any servers whose nicknames appear in
           'excludeServers' are left off the directory entirely.
        """
        try:
            self._lock()
            self.clean()
            if publicationTime is None:
                publicationTime = time.time()
            if previousMidnight(startAt) >= previousMidnight(endAt):
                raise MixError("Validity range does not contain a full day.")

            excludeServers = [nickname.lower() for nickname in excludeServers]

            # First, sort all servers by nickname.
            includedByNickname = {}
            for fn, s in self.servers.items():
                nickname = s.getNickname().lower()
                if nickname in excludeServers: continue
                includedByNickname.setdefault(nickname, []).append((s, fn))

            # Second, find all servers that are valid for part of the period,
            # and that aren't superseded for the whole period.
            timeRange = IntervalSet([(previousMidnight(startAt),
                                      endAt + extraTime)])

            for nickname, ss in includedByNickname.items():
                # We prefer the most-recently-published descriptor.  If two
                # are published at the same time, we prefer the one that
                # expires last.
                ss = [(s['Server']['Published'], s['Server']['Valid-Until'], s,
                       fn) for s, fn in ss]
                ss.sort()
                ss.reverse()
                uncovered = timeRange.copy()
                included = []
                for _, _, s, fn in ss:
                    valid = s.getIntervalSet()
                    if (uncovered * valid):
                        included.append((s, fn))
                        uncovered -= valid
                includedByNickname[nickname] = included

            # Now sort the remaining servers by nickname, then by valid-after.
            included = []
            for ss in includedByNickname.values():
                for s, fn in ss:
                    nickname = s.getNickname()
                    validAfter = s['Server']['Valid-After']
                    included.append((nickname, validAfter, fn))
            included.sort()

            # FFFF We should probably not do all of this in RAM, but
            # FFFF what the hey.  It will only matter if we have many, many
            # FFFF servers in the system.
            contents = []
            for _, _, fn in included:
                txt = readFile(os.path.join(self.serverDir, fn))
                contents.append(txt)

            goodServers = [n for n, _, _ in included if n not in badServers]
            g = {}
            for n in goodServers:
                g[n] = 1
            goodServers = g.keys()
            goodServers.sort()
            goodServers = ", ".join(goodServers)

            clientVersions = self.config['Directory']['ClientVersions']
            serverVersions = self.config['Directory']['ServerVersions']

            #FFFF Support for multiple signatures
            header = """\
            [Directory]
            Version: 0.2
            Published: %s
            Valid-After: %s
            Valid-Until: %s
            Recommended-Servers: %s
            [Signature]
            DirectoryIdentity: %s
            DirectoryDigest:
            DirectorySignature:
            [Recommended-Software]
            MixminionClient: %s
            MixminionServer: %s
            """ % (formatTime(publicationTime), formatDate(startAt),
                   formatDate(endAt), goodServers,
                   formatBase64(pk_encode_public_key(identityKey)),
                   ", ".join(clientVersions), ", ".join(serverVersions))

            directory = header + "".join(contents)
            directory = _getDirectoryDigestImpl(directory, identityKey)

            # Make sure that the directory checks out
            # FFFF remove this once we are _very_ confident.
            if 1:
                parsed = ServerDirectory(string=directory)
                includedDigests = {}
                for _, _, fn in included:
                    includedDigests[self.servers[fn]['Server']['Digest']] = 1
                foundDigests = {}
                for s in parsed.getAllServers():
                    foundDigests[s['Server']['Digest']] = 1
                assert foundDigests == includedDigests

            writeFile(os.path.join(self.baseDir, "directory"),
                      directory,
                      mode=0644)

            f, _ = openUnique(
                os.path.join(self.dirArchiveDir, "dir-" + formatFnameTime()))
            f.write(directory)
            f.close()
        finally:
            self._unlock()