Example #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)
Example #2
0
def _writeEncryptedFile(fname, password, magic, data):
    """Write 'data' into an encrypted file named 'fname', replacing it
       if necessary.  Encrypts the data with the password 'password',
       and uses the filetype 'magic'."""
    assert len(magic) == MAGIC_LEN
    prng = getCommonPRNG()
    length = struct.pack("!L", len(data))
    paddingLen = ceilDiv(len(data), 1024)*1024 - len(data)
    padding = prng.getBytes(paddingLen)
    data = "".join([length,data,padding])
    salt = prng.getBytes(SALT_LEN)
    key = sha1(salt+password+salt)[:AES_KEY_LEN]
    digest = sha1("".join([data,salt,magic]))
    encrypted = ctr_crypt(data+digest, key)
    contents = "".join([magic,"\x00",salt,encrypted])
    writeFile(fname, armorText(contents,
                               "TYPE III KEYRING", [("Version","0.1")]))
Example #3
0
def _writeEncryptedFile(fname, password, magic, data):
    """Write 'data' into an encrypted file named 'fname', replacing it
       if necessary.  Encrypts the data with the password 'password',
       and uses the filetype 'magic'."""
    assert len(magic) == MAGIC_LEN
    prng = getCommonPRNG()
    length = struct.pack("!L", len(data))
    paddingLen = ceilDiv(len(data), 1024) * 1024 - len(data)
    padding = prng.getBytes(paddingLen)
    data = "".join([length, data, padding])
    salt = prng.getBytes(SALT_LEN)
    key = sha1(salt + password + salt)[:AES_KEY_LEN]
    digest = sha1("".join([data, salt, magic]))
    encrypted = ctr_crypt(data + digest, key)
    contents = "".join([magic, "\x00", salt, encrypted])
    writeFile(fname,
              armorText(contents, "TYPE III KEYRING", [("Version", "0.1")]))
Example #4
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
Example #5
0
    def updateKeys(self, packetHandler, statusFile=None,when=None):
        """Update the keys stored in a PacketHandler,
           MMTPServer object, so that they contain the currently correct
           keys.  Also removes any dead keys.

           This function is idempotent.
        """
        self.checkKeys()
        deadKeys = self.getDeadKeys(when)
        self.currentKeys = keys = self.getServerKeysets(when)
        keyNames = [k.keyname for k in keys]
        deadKeyNames = [k.keyname for msg, k in deadKeys]
        LOG.info("Updating keys: %s currently valid (%s); %s expired (%s)",
                 len(keys), " ".join(keyNames),
                 len(deadKeys), " ".join(deadKeyNames))
        if packetHandler is not None:
            packetKeys = []
            hashLogs = []

            for k in keys:
                packetKeys.append(k.getPacketKey())
                hashLogs.append(k.getHashLog())
            packetHandler.setKeys(packetKeys, hashLogs)

        if statusFile:
            writeFile(statusFile,
                    "".join(["%s\n"%k.getDescriptorFileName() for k in keys]),
                    0644)

        for msg, ks in deadKeys:
            LOG.info(msg)
            ks.delete()

        if deadKeys:
            self.checkKeys()

        self.nextUpdate = None
        self.getNextKeyRotation(keys)
Example #6
0
 def markAsPublished(self):
     """Mark this keyset as published."""
     contents = "%s\n" % formatTime(time.time(), 1)
     writeFile(self.publishedFile, contents, mode=0600)
     self.published = 1
Example #7
0
          [Testing]
          Platform: %s
          Configuration: %s
          """ % (getPlatformSummary(), config.getConfigurationSummary())

    # Remove extra (leading or trailing) whitespace from the lines.
    lines = [line.strip() for line in info.split("\n")]
    # Remove empty lines
    lines = filter(None, lines)
    # Force a newline at the end of the file, rejoin, and sign.
    lines.append("")
    info = "\n".join(lines)
    info = signServerInfo(info, identityKey)

    # Write the desciptor
    writeFile(serverKeys.getDescriptorFileName(), info, mode=0644)

    # This is for debugging: we try to parse and validate the descriptor
    #   we just made.
    # FFFF Remove this once we're more confident.
    inf = ServerInfo(string=info)
    ok = checkDescriptorConsistency(inf, config, log=0, isPublished=0)
    if ok not in ('good', 'so-so'):
        print "========"
        print info
        print "======"
        checkDescriptorConsistency(inf, config, log=1, isPublished=0)
    assert ok in ('good', 'so-so')

    return info
Example #8
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()
Example #9
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()
Example #10
0
 def markAsPublished(self):
     """Mark this keyset as published."""
     contents = "%s\n"%formatTime(time.time(),1)
     writeFile(self.publishedFile, contents, mode=0600)
     self.published = 1
Example #11
0
          Platform: %s
          Configuration: %s
          """ %(getPlatformSummary(),
                config.getConfigurationSummary())

    # Remove extra (leading or trailing) whitespace from the lines.
    lines = [ line.strip() for line in info.split("\n") ]
    # Remove empty lines
    lines = filter(None, lines)
    # Force a newline at the end of the file, rejoin, and sign.
    lines.append("")
    info = "\n".join(lines)
    info = signServerInfo(info, identityKey)

    # Write the desciptor
    writeFile(serverKeys.getDescriptorFileName(), info, mode=0644)

    # This is for debugging: we try to parse and validate the descriptor
    #   we just made.
    # FFFF Remove this once we're more confident.
    inf = ServerInfo(string=info)
    ok = checkDescriptorConsistency(inf, config, log=0, isPublished=0)
    if ok not in ('good', 'so-so'):
        print "========"
        print info
        print "======"
        checkDescriptorConsistency(inf, config, log=1, isPublished=0)
    assert ok in ('good', 'so-so')

    return info