summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2008-09-11 19:57:46 +0000
committerNick Mathewson <nickm@torproject.org>2008-09-11 19:57:46 +0000
commit3b22d6a1fb41ede3b9f5afb84ecd78208fca8559 (patch)
tree1aa8c871f0c3c2aa626f348ccd88cd7c05d8de91
parentc27349f728b2c8e749b7fd6fd13fad9c05c0cc04 (diff)
Initial glider TODO and format-handling code
git-svn-id: file:///home/or/svnrepo/updater/trunk@16856 55e972cd-5a19-0410-ae62-a4d7a52db4cd
-rw-r--r--TODO37
-rw-r--r--lib/glider/__init__.py3
-rw-r--r--lib/glider/formats.py166
3 files changed, 206 insertions, 0 deletions
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)))
+"""