summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2008-09-16 16:07:40 +0000
committerNick Mathewson <nickm@torproject.org>2008-09-16 16:07:40 +0000
commitddc03061218dc00a664aaf10a6a2fec2b604deac (patch)
tree4cb3bc48ed2bc1542c82c7711133c99c7c285368
parent96516246c3a3cdebf149977b52e14a919b401a33 (diff)
More glider hacks: implement public keys.
git-svn-id: file:///home/or/svnrepo/updater/trunk@16917 55e972cd-5a19-0410-ae62-a4d7a52db4cd
-rw-r--r--lib/glider/formats.py1
-rw-r--r--lib/glider/keys.py103
-rw-r--r--lib/glider/tests.py2
-rw-r--r--lib/sexp/access.py10
4 files changed, 96 insertions, 20 deletions
diff --git a/lib/glider/formats.py b/lib/glider/formats.py
index 4659e10..6ccd466 100644
--- a/lib/glider/formats.py
+++ b/lib/glider/formats.py
@@ -35,6 +35,7 @@ def rolePathMatches(rolePath, path):
try:
regex = _rolePathCache[rolePath]
except KeyError:
+ rolePath = re.sub(r'/+', '/', rolePath)
rolePath = re.escape(rolePath).replace(r'\*\*', r'.*')
rolePath = rolePath.replace(r'\*', r'[^/]*')
rolePath += "$"
diff --git a/lib/glider/keys.py b/lib/glider/keys.py
index 1ff96f1..9fb73ec 100644
--- a/lib/glider/keys.py
+++ b/lib/glider/keys.py
@@ -33,13 +33,33 @@ class PublicKey:
def getRoles(self):
raise NotImplemented()
-def intToBinary(number):
- h = hex(number)
- assert h[:2] == '0x'
- return binascii.a2b_hex(h[2:])
+if hex(1L).upper() == "0X1L":
+ def intToBinary(number):
+ """Convert an int or long into a big-endian series of bytes.
+ """
+ # This "convert-to-hex, then use binascii" approach may look silly,
+ # but it's over 10x faster than the Crypto.Util.number approach.
+ h = hex(long(number))
+ h = h[2:-1]
+ if len(h)%2:
+ h = "0"+h
+ return binascii.a2b_hex(h)
+elif hex(1L).upper() == "0X1":
+ def intToBinary(number):
+ h = hex(long(number))
+ h = h[2:]
+ if len(h)%2:
+ h = "0"+h
+ return binascii.a2b_hex(h)
+else:
+ import Crypto.Util.number
+ intToBinary = Crypto.Util.number.long_to_bytes
+ assert None
def binaryToInt(binary):
- return int(binascii.b2a_hex(binary), 16)
+ """Convert a big-endian series of bytes into a long.
+ """
+ return long(binascii.b2a_hex(binary), 16)
def _pkcs1_padding(m, size):
@@ -48,12 +68,39 @@ def _pkcs1_padding(m, size):
# verification with nondeterministic padding. "argh."
s = [ "\x00\x01", "\xff"* (size-3-len(m)), "\x00", m ]
- r = s.join()
+ r = "".join(s)
return r
+def _xor(a,b):
+ if a:
+ return not b
+ else:
+ return b
+
class RSAKey(PublicKey):
+ """
+ >>> k = RSAKey.generate(bits=512)
+ >>> sexpr = k.format()
+ >>> sexpr[:2]
+ ('pubkey', [('type', 'rsa')])
+ >>> k1 = RSAKey.fromSExpression(sexpr)
+ >>> k1.key.e == k.key.e
+ True
+ >>> k1.key.n == k.key.n
+ True
+ >>> k.getKeyID() == k1.getKeyID()
+ True
+ >>> s = ['tag1', ['foobar'], [['foop', 'bar']], 'baz']
+ >>> method, sig = k.sign(sexpr=s)
+ >>> k.checkSignature(method, sig, sexpr=s)
+ True
+ >>> s2 = [ s ]
+ >>> k.checkSignature(method, sig, sexpr=s2)
+ False
+ """
def __init__(self, key):
self.key = key
+ self.keyid = None
@staticmethod
def generate(bits=2048):
@@ -63,8 +110,8 @@ class RSAKey(PublicKey):
@staticmethod
def fromSExpression(sexpr):
# sexpr must match PUBKEY_SCHEMA
- typeattr = s_child(sexpr[1], "type")[1]
- if typeattr[1] != "rsa":
+ typeattr = sexp.access.s_attr(sexpr[1], "type")
+ if typeattr != "rsa":
return None
if len(sexpr[2]) != 2:
raise PubkeyFormatException("RSA keys must have an e,n pair")
@@ -77,18 +124,36 @@ class RSAKey(PublicKey):
e = intToBinary(self.key.e)
return ("pubkey", [("type", "rsa")], (e, n))
- def sign(self, sexpr):
- d_obj = Crypto.Digest.SHA256.new()
- sexpr.encode.hash_canonical(sexpr, d_obj)
- m = _pkcs1_padding(d_obj.digest(), (self.key.size()+1) // 8)
- return ("sha256-pkcs1", self.key.sign(m, "")[0])
-
- def checkSignature(self, method, sexpr, sig):
+ def getKeyID(self):
+ if self.keyid == None:
+ n = intToBinary(self.key.n)
+ e = intToBinary(self.key.e)
+ keyval = (e,n)
+ d_obj = Crypto.Hash.SHA256.new()
+ sexp.encode.hash_canonical(keyval, d_obj)
+ self.keyid = ("rsa", d_obj.digest())
+ return self.keyid
+
+ def sign(self, sexpr=None, digest=None):
+ assert _xor(sexpr == None, digest == None)
+ if digest == None:
+ d_obj = Crypto.Hash.SHA256.new()
+ sexp.encode.hash_canonical(sexpr, d_obj)
+ digest = d_obj.digest()
+ m = _pkcs1_padding(digest, (self.key.size()+1) // 8)
+ sig = intToBinary(self.key.sign(m, "")[0])
+ return ("sha256-pkcs1", sig)
+
+ def checkSignature(self, method, sig, sexpr=None, digest=None):
+ assert _xor(sexpr == None, digest == None)
if method != "sha256-pkcs1":
raise UnknownMethod("method")
- d_obj = Crypto.Digest.SHA256.new()
- sexpr.encode.hash_canonical(sexpr, d_obj)
- m = _pkcs1_padding(d_obj.digest(), (self.key.size()+1) // 8)
- return self.key.verify(sig, m)
+ if digest == None:
+ d_obj = Crypto.Hash.SHA256.new()
+ sexp.encode.hash_canonical(sexpr, d_obj)
+ digest = d_obj.digest()
+ sig = binaryToInt(sig)
+ m = _pkcs1_padding(digest, (self.key.size()+1) // 8)
+ return self.key.verify(m, (sig,))
diff --git a/lib/glider/tests.py b/lib/glider/tests.py
index 074e0be..d149678 100644
--- a/lib/glider/tests.py
+++ b/lib/glider/tests.py
@@ -15,7 +15,7 @@ def suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(glider.formats))
- #suite.addTest(doctest.DocTestSuite(sexp.parse))
+ suite.addTest(doctest.DocTestSuite(glider.keys))
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromModule(glider.tests))
diff --git a/lib/sexp/access.py b/lib/sexp/access.py
index beb87c9..c5182f0 100644
--- a/lib/sexp/access.py
+++ b/lib/sexp/access.py
@@ -36,6 +36,16 @@ def s_child(s, tag):
return child
return None
+def s_attr(s, tag):
+ """Returns the second element of the child of 's' whose tag is 'tag'.
+ This is helpful for extracting a (key val) element. Returns None
+ if there is no such element.
+ """
+ ch = s_child(s,tag)
+ if ch == None or len(ch) < 2:
+ return None
+ return ch[1]
+
def s_children(s, tag):
"""Returns a generator yielding all children of 's' whose tag is 'tag'.