From 3b22d6a1fb41ede3b9f5afb84ecd78208fca8559 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 11 Sep 2008 19:57:46 +0000 Subject: Initial glider TODO and format-handling code git-svn-id: file:///home/or/svnrepo/updater/trunk@16856 55e972cd-5a19-0410-ae62-a4d7a52db4cd --- TODO | 37 +++++++++++ lib/glider/__init__.py | 3 + lib/glider/formats.py | 166 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 TODO create mode 100644 lib/glider/__init__.py create mode 100644 lib/glider/formats.py diff --git a/TODO b/TODO new file mode 100644 index 0000000..48050c0 --- /dev/null +++ b/TODO @@ -0,0 +1,37 @@ + + +o Write spec + +. Write server-side code (python) + o S-expression lib + . Code to manage data formats + - Code to wrangle private keys + - Generate + - Store, load (password-protected) + - Print for posterity + + - Code to generate timestamp files + - Code to generate mirror files + - Code to generate keylist files, and add new keys to them, and + remove keys. + +- Write client-side code + - Decide early if a python implementation will do for v1. + IF SO: + - Adjust httplib, urllib2 to use socks4a. + - Check SOCKS package for suitability as basis for socks4a support? + - Look into best packageing practices + IF NOT: + - Maybe use curllib for downloading, unless there's something + better. + - Check out Ron's reference code for s-expression handling. + + - Write cacheing code + - Write code to pick a mirror + - Write code to grab a timestamp file and figure out what to do. + - Write code to update other files + - Write code to run, telling another process about status, + eventually coming up with a list of packages to install or an + "A-OK" signal. + + - GUI diff --git a/lib/glider/__init__.py b/lib/glider/__init__.py new file mode 100644 index 0000000..e05bafe --- /dev/null +++ b/lib/glider/__init__.py @@ -0,0 +1,3 @@ + +__all__ = [ 'formats' ] + diff --git a/lib/glider/formats.py b/lib/glider/formats.py new file mode 100644 index 0000000..b579848 --- /dev/null +++ b/lib/glider/formats.py @@ -0,0 +1,166 @@ + +import OpenSSL.crypto + +import sexp.access +import sexp.encode +import time +import re + +class UnknownMethod(Exception): + pass + +class PublicKey: + def format(self): + raise NotImplemented() + def sign(self, data): + # returns a list of method,signature tuples. + raise NotImplemented() + def checkSignature(self, method, data, signature): + # returns True, False, or raises UnknownMethod. + raise NotImplemented() + def getKeyID(self): + raise NotImplemented() + def getRoles(self): + raise NotImplemented() + +class KeyDB: + def __init__(self): + self.keys = {} + def addKey(self, k): + self.keys[k.getKeyID()] = k + def getKey(self, keyid): + return self.keys[keyid] + +def rolePathMatches(rolePath, path): + """ + + >>> rolePath.matches("a/b/c/", "a/b/c/") + True + >>> rolePath.matches("**/c.*", "a/b/c.txt") + True + """ + rolePath = re.escape(rolePath).replace(r'\*\*', r'.*') + rolePath = rolePath.replace(r'\*', r'[^/]*') + rolePath += "$" + return re.match(rolePath, path) != None + +def checkSignatures(signed, keyDB, role, path): + goodSigs = [] + badSigs = [] + unknownSigs = [] + tangentialSigs = [] + + for signature in sexp.access.s_children(signed, "signature"): + attrs = signature[1] + sig = attrs[2] + keyid = s_child(attrs, "keyid")[1] + try: + key = keyDB.getKey(keyid) + except KeyError: + unknownSigs.append(keyid) + continue + method = s_child(attrs, "method")[1] + try: + result = key.checkSignature(method, data, sig) + except UnknownMethod: + continue + if result == True: + if role is not None: + for r,p in key.getRoles(): + if r == role and rolePathMatches(p, path): + break + else: + tangentialSigs.append(sig) + continue + + goodSigs.append(keyid) + else: + badSigs.append(keyid) + +def sign(signed, key): + assert sexp.access.s_tag(signed) == 'signed' + s = signed[1] + keyid = key.keyID() + + oldsignatures = [ s for s in signed[2:] if s_child(s[1], "keyid") != keyid ] + signed[2:] = oldsignatures + + for method, sig in key.sign(s): + signed.append(['signature', [['keyid', keyid], ['method', method]] + sig]) + +def formatTime(t): + return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t)) + +def parseTime(s): + return time.timegm(time.strptime(s, "%Y-%m-%d %H:%M:%S")) + + +TIME_SCHEMA = r"""/\{d}4-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/""" + +ATTRS_SCHEMA = r"""(:anyof (_ *))""" + +SIGNED_SCHEMA = r""" + (=signed + _ + (:someof + (=signature ((:unordered + (=keyid _) (=method _) .ATTRS)) _) + ) + )""" + +KEYFILE_SCHEMA = r""" + (=keylist + (=ts .TIME) + (=keys + (:anyof + (=key ((:unordered (=roles (:someof (. .))) .ATTRS)) _) + )) + * + )""" + +MIRRORLIST_SCHEMA = r""" + (=mirrorlist + (=ts .TIME) + (=mirrors (:anyof + (=mirror ((:unordered (=name .) (=urlbase .) (=contents (:someof .)) + .ATTRS))))) + *) +""" + +TIMESTAMP_SCHEMA = r""" + (=ts + ((:unordered (=at .TIME) (=m .TIME .) (=k .TIME .) + (:anyof (=b . . .TIME . .)) .ATTRS)) + )""" + +BUNDLE_SCHEMA = r""" + (=bundle + (=at .TIME) + (=os .) + (:maybe (=arch .)) + (=packages + (:someof + (. . . . ((:unordered + (:maybe (=order . . .)) + (:maybe (=optional)) + (:anyof (=gloss . .)) + (:anyof (=longgloss . .)) + .ATTRS))) + ) + ) + * + )""" + +PACKAGE_SCHEMA = r""" + (=package + ((:unordred (=name .) + (=version .) + (=format . (.ATTRS)) + (=path .) + (=ts .TIME) + (=digest .) + (:anyof (=shortdesc . .)) + (:anyof (=longdesc . .)) + .ATTRS))) +""" -- cgit v1.2.3