source: fedd/fedd_util.py @ fca8740

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since fca8740 was b234bb9, checked in by Ted Faber <faber@…>, 16 years ago

Experiment creation integrated

  • Property mode set to 100644
File size: 6.5 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4
5from M2Crypto import SSL, X509, EVP
6from pyasn1.codec.der import decoder
7
8
9# The version of M2Crypto on users is pretty old and doesn't have all the
10# features that are useful.  The legacy code is somewhat more brittle than the
11# main line, but will work.
12if "as_der" not in dir(EVP.PKey):
13    from asn1_raw import get_key_bits_from_file, get_key_bits_from_cert
14    legacy = True
15else:
16    legacy = False
17
18class fedd_ssl_context(SSL.Context):
19    """
20    Simple wrapper around an M2Crypto.SSL.Context to initialize it for fedd.
21    """
22    def __init__(self, my_cert, trusted_certs=None, password=None):
23        """
24        construct a fedd_ssl_context
25
26        @param my_cert: PEM file with my certificate in it
27        @param trusted_certs: PEM file with trusted certs in it (optional)
28        """
29        SSL.Context.__init__(self)
30
31        # load_cert takes a callback to get a password, not a password, so if
32        # the caller provided a password, this creates a nonce callback using a
33        # lambda form.
34        if password != None and not callable(password):
35            # This is cute.  password = lambda *args: password produces a
36            # function object that returns itself rather than one that returns
37            # the object itself.  This is because password is an object
38            # reference and after the assignment it's a lambda.  So we assign
39            # to a temp.
40            pwd = password
41            password =lambda *args: pwd
42
43        if password != None:
44            self.load_cert(my_cert, callback=password)
45        else:
46            self.load_cert(my_cert)
47        if trusted_certs != None: self.load_verify_locations(trusted_certs)
48        self.set_verify(SSL.verify_peer, 10)
49
50class fedid:
51    """
52    Wrapper around the federated ID from an X509 certificate.
53    """
54    HASHSIZE=20
55    def __init__(self, bits=None, hexstr=None, cert=None, file=None):
56        if bits != None:
57            self.set_bits(bits)
58        elif hexstr != None:
59            self.set_hexstr(hexstr)
60        elif cert != None:
61            self.set_cert(cert)
62        elif file != None:
63            self.set_file(file)
64        else:
65            self.buf = None
66
67    def __hash__(self):
68        return hash(self.buf)
69
70    def __eq__(self, other):
71        if isinstance(other, type(self)):
72            return self.buf == other.buf
73        elif isinstance(other, type(str())):
74            return self.buf == other;
75        else:
76            return False
77
78    def __ne__(self, other): return not self.__eq__(other)
79
80    def __str__(self):
81        if self.buf != None:
82            return str().join([ "%02x" % ord(x) for x in self.buf])
83        else: return ""
84
85    def __repr__(self):
86        return "fedid(hexstr='%s')" % self.__str__()
87
88    def pack_soap(self):
89        return self.buf
90
91    def digest_bits(self, bits):
92        """Internal function.  Compute the fedid from bits and store in buf"""
93        d = EVP.MessageDigest('sha1')
94        d.update(bits)
95        self.buf = d.final()
96
97
98    def set_hexstr(self, hexstr):
99        h = hexstr.replace(':','')
100        self.buf= str().join([chr(int(h[i:i+2],16)) \
101                for i in range(0,2*fedid.HASHSIZE,2)])
102
103    def get_hexstr(self):
104        """Return the hexstring representation of the fedid"""
105        return __str__(self)
106
107    def set_bits(self, bits):
108        """Set the fedid to bits(a 160 bit buffer)"""
109        self.buf = bits
110
111    def get_bits(self):
112        """Get the 160 bit buffer from the fedid"""
113        return self.buf
114
115    def set_file(self, file):
116        """Get the fedid from a certificate file
117
118        Calculate the SHA1 hash over the bit string of the public key as
119        defined in RFC3280.
120        """
121        self.buf = None
122        if legacy: self.digest_bits(get_key_bits_from_file(file))
123        else: self.set_cert(X509.load_cert(file))
124
125    def set_cert(self, cert):
126        """Get the fedid from a certificate.
127
128        Calculate the SHA1 hash over the bit string of the public key as
129        defined in RFC3280.
130        """
131
132        self.buf = None
133        if (cert != None):
134            if legacy:
135                self.digest_bits(get_key_bits_from_cert(cert))
136            else:
137                b = []
138                k = cert.get_pubkey()
139
140                # Getting the key was easy, but getting the bit string of the
141                # key requires a side trip through ASN.1
142                dec = decoder.decode(k.as_der())
143
144                # kv is a tuple of the bits in the key.  The loop below
145                # recombines these into bytes and then into a buffer for the
146                # SSL digest function.
147                kv =  dec[0].getComponentByPosition(1)
148                for i in range(0, len(kv), 8):
149                    v = 0
150                    for j in range(0, 8):
151                        v = (v << 1) + kv[i+j]
152                    b.append(v)
153                # The comprehension turns b from a list of bytes into a buffer
154                # (string) of bytes
155                self.digest_bits(str().join([chr(x) for x in b]))
156
157def pack_id(id):
158    """
159    Return a dictionary with the field name set by the id type.  Handy for
160    creating dictionaries to be converted to messages.
161    """
162    if isinstance(id, type(fedid())): return { 'fedid': id }
163    elif id.startswith("http:") or id.startswith("https:"): return { 'uri': id }
164    else: return { 'username': id}
165
166def unpack_id(id):
167    """return id as a type determined by the key"""
168    if id.has_key("fedid"): return fedid(id["fedid"])
169    else:
170        for k in ("username", "uri", "kerberosUsername"):
171            if id.has_key(k): return id[k]
172    return None
173
174def pack_soap(container, name, contents):
175    """
176    Convert the dictionary in contents into a tree of ZSI classes.
177
178    The holder classes are constructed from factories in container and assigned
179    to either the element or attribute name.  This is used to recursively
180    create the SOAP message.
181    """
182    if getattr(contents, "__iter__", None) != None:
183        attr =getattr(container, "new_%s" % name, None)
184        if attr: obj = attr()
185        else:
186            print dir(container)
187            raise TypeError("%s does not have a new_%s attribute" % \
188                    (container, name))
189        for e, v in contents.iteritems():
190            assign = getattr(obj, "set_element_%s" % e, None) or \
191                    getattr(obj, "set_attribute_%s" % e, None)
192            if isinstance(v, type(dict())):
193                assign(pack_soap(obj, e, v))
194            elif getattr(v, "__iter__", None) != None:
195                assign([ pack_soap(obj, e, val ) for val in v])
196            elif getattr(v, "pack_soap", None) != None:
197                assign(v.pack_soap())
198            else:
199                assign(v)
200        return obj
201    else: return contents
202
203def unpack_soap(element):
204    """
205    Convert a tree of ZSI SOAP classes intro a hash.  The inverse of pack_soap
206
207    Elements or elements that are empty are ignored.
208    """
209    methods = [ m for m in dir(element) \
210            if m.startswith("get_element") or m.startswith("get_attribute")]
211    if len(methods) > 0:
212        rv = { }
213        for m in methods:
214            if m.startswith("get_element_"): n = m.replace("get_element_","",1)
215            else: n = m.replace("get_attribute_", "", 1)
216            sub = getattr(element, m)()
217            if sub != None:
218                if isinstance(sub, basestring):
219                    rv[n] = sub
220                elif getattr(sub, "__iter__", None) != None:
221                    if len(sub) > 0: rv[n] = [unpack_soap(e) for e in sub]
222                else:
223                    rv[n] = unpack_soap(sub)
224        return rv
225    else: 
226        return element
Note: See TracBrowser for help on using the repository browser.