diff options
Diffstat (limited to 'lib/glider/keys.py')
-rw-r--r-- | lib/glider/keys.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/lib/glider/keys.py b/lib/glider/keys.py new file mode 100644 index 0000000..1ff96f1 --- /dev/null +++ b/lib/glider/keys.py @@ -0,0 +1,94 @@ + +# These require PyCrypto. +import Crypto.PublicKey.RSA +import Crypto.Hash.SHA256 + +import sexp.access +import sexp.encode +import sexp.parse + +import binascii +import os + +class CryptoError(Exception): + pass + +class PubkeyFormatException(Exception): + pass + +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() + +def intToBinary(number): + h = hex(number) + assert h[:2] == '0x' + return binascii.a2b_hex(h[2:]) + +def binaryToInt(binary): + return int(binascii.b2a_hex(binary), 16) + +def _pkcs1_padding(m, size): + + # I'd rather use OAEP+, but apparently PyCrypto barely supports + # signature verification, and doesn't seem to support signature + # verification with nondeterministic padding. "argh." + + s = [ "\x00\x01", "\xff"* (size-3-len(m)), "\x00", m ] + r = s.join() + return r + +class RSAKey(PublicKey): + def __init__(self, key): + self.key = key + + @staticmethod + def generate(bits=2048): + key = Crypto.PublicKey.RSA.generate(bits=bits, randfunc=os.urandom) + return RSAKey(key) + + @staticmethod + def fromSExpression(sexpr): + # sexpr must match PUBKEY_SCHEMA + typeattr = s_child(sexpr[1], "type")[1] + if typeattr[1] != "rsa": + return None + if len(sexpr[2]) != 2: + raise PubkeyFormatException("RSA keys must have an e,n pair") + e,n = sexpr[2] + key = Crypto.PublicKey.RSA.construct((binaryToInt(n), binaryToInt(e))) + return RSAKey(key) + + def format(self): + n = intToBinary(self.key.n) + 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): + 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) + + |