import io import ecdsa import hashlib import yaml import base64 import sys def render_path(path): if path: return "'" + ", ".join(path) + "'" else: return "the top level" class ErrorHandlingDict(dict): def __init__(self, filename, path): self._filename = filename self._path = path dict.__init__({}) def __missing__(self, key): path = render_path(self._path) print >>sys.stderr, "error: could not find configuration key '%s' at %s in %s" % (key, path, self._filename) sys.exit(1) def errorhandlify(term, filename, path=[]): if isinstance(term, basestring): return term elif isinstance(term, int): return term elif isinstance(term, dict): result = ErrorHandlingDict(filename, path) for k, v in term.items(): result[k] = errorhandlify(v, filename, path + [k]) return result elif isinstance(term, list): return [errorhandlify(e, filename, path + ["item %d" % i]) for i, e in enumerate(term, start=1)] else: print "unknown type", type(term) sys.exit(1) def verify_config(rawconfig, signature, publickey_base64, filename): publickey = base64.decodestring(publickey_base64) try: vk = ecdsa.VerifyingKey.from_der(publickey) vk.verify(signature, rawconfig, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der) except ecdsa.keys.BadSignatureError: print >>sys.stderr, "error: configuration file %s did not have a correct signature" % (filename,) sys.exit(1) return errorhandlify(yaml.load(io.BytesIO(rawconfig), yaml.SafeLoader), filename) def verify_and_read_config(filename, publickey_base64): rawconfig = open(filename).read() signature = open(filename + ".sig").read() verify_config(rawconfig, signature, publickey_base64, filename) config = yaml.load(io.BytesIO(rawconfig), yaml.SafeLoader) return errorhandlify(config, filename) def read_config(filename): config = yaml.load(open(filename), yaml.SafeLoader) return errorhandlify(config, filename)