From c0d8aceccb0961a25ee58a163441bbcbe6d6ea3d Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Fri, 27 Jan 2017 16:11:11 +0100 Subject: Verify config file signature Read log key from config file in more places. Check STH signature in storagegc.py --- test/catlfish-test-local-1.cfg | 4 +++ test/catlfish-test-local-merge-2.cfg | 4 +++ test/catlfish-test-local-merge.cfg | 4 +++ test/catlfish-test-local-signing.cfg | 4 +++ test/logadminkey-private.pem | 5 +++ test/logadminkey.pem | 4 +++ test/scripts/light-system-test-prepare.sh | 2 +- tools/compileconfig.py | 10 +++--- tools/loginfo.py | 5 ++- tools/merge_sth.py | 3 +- tools/mergetools.py | 6 ++-- tools/readconfig.py | 54 +++++++++++++++++++++++++++++++ tools/storagegc.py | 7 ++-- 13 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 test/logadminkey-private.pem create mode 100644 test/logadminkey.pem create mode 100644 tools/readconfig.py diff --git a/test/catlfish-test-local-1.cfg b/test/catlfish-test-local-1.cfg index adc3e84..1795649 100644 --- a/test/catlfish-test-local-1.cfg +++ b/test/catlfish-test-local-1.cfg @@ -30,5 +30,9 @@ paths: ratelimits: add_chain: 10 per second +logadminkey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ + Ah9sZ2CD+JeLbprS6AFcZbo0TGCH0rtEnr2Q3JW0ylhfA+ + 0/WLu755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== + #options: # - sctcaching diff --git a/test/catlfish-test-local-merge-2.cfg b/test/catlfish-test-local-merge-2.cfg index 579e360..b871313 100644 --- a/test/catlfish-test-local-merge-2.cfg +++ b/test/catlfish-test-local-merge-2.cfg @@ -18,3 +18,7 @@ paths: publickeys: publickeys logpublickey: keys/logkey.pem privatekeys: privatekeys + +logadminkey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ + Ah9sZ2CD+JeLbprS6AFcZbo0TGCH0rtEnr2Q3JW0ylhfA+ + 0/WLu755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== diff --git a/test/catlfish-test-local-merge.cfg b/test/catlfish-test-local-merge.cfg index 273b68e..3b4d45f 100644 --- a/test/catlfish-test-local-merge.cfg +++ b/test/catlfish-test-local-merge.cfg @@ -8,3 +8,7 @@ paths: privatekeys: privatekeys verifycert_bin: ../bin/verifycert.erl.escript known_roots: known_roots/ + +logadminkey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ + Ah9sZ2CD+JeLbprS6AFcZbo0TGCH0rtEnr2Q3JW0ylhfA+ + 0/WLu755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== diff --git a/test/catlfish-test-local-signing.cfg b/test/catlfish-test-local-signing.cfg index 386001e..df91bcd 100644 --- a/test/catlfish-test-local-signing.cfg +++ b/test/catlfish-test-local-signing.cfg @@ -19,3 +19,7 @@ paths: # slot: 0 # label: mylabel # pin: ffff + +logadminkey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ + Ah9sZ2CD+JeLbprS6AFcZbo0TGCH0rtEnr2Q3JW0ylhfA+ + 0/WLu755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== diff --git a/test/logadminkey-private.pem b/test/logadminkey-private.pem new file mode 100644 index 0000000..4464169 --- /dev/null +++ b/test/logadminkey-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIKoa7p5gnAgd9L2t5N1L73MJ1OCj/zItvGoVMtsKPy4JoAoGCCqGSM49 +AwEHoUQDQgAEQAh9sZ2CD+JeLbprS6AFcZbo0TGCH0rtEnr2Q3JW0ylhfA+0/WLu +755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== +-----END EC PRIVATE KEY----- diff --git a/test/logadminkey.pem b/test/logadminkey.pem new file mode 100644 index 0000000..43ff2c4 --- /dev/null +++ b/test/logadminkey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQAh9sZ2CD+JeLbprS6AFcZbo0TGC +H0rtEnr2Q3JW0ylhfA+0/WLu755b3soVX/wI23vqCVGC7N9fOB2WUltveQ== +-----END PUBLIC KEY----- diff --git a/test/scripts/light-system-test-prepare.sh b/test/scripts/light-system-test-prepare.sh index df45d25..84b06cd 100755 --- a/test/scripts/light-system-test-prepare.sh +++ b/test/scripts/light-system-test-prepare.sh @@ -50,7 +50,6 @@ printf 0 > mergedb-secondary/verifiedsize mkdir known_roots cp ${top_srcdir}/tools/testcerts/roots/* known_roots mkdir privatekeys -mkdir publickeys echo "apikeys:" > api-keys.cfg for node in ${NODES}; do \ (cd privatekeys ; ${top_srcdir}/tools/create-key.sh ${node}) @@ -66,6 +65,7 @@ echo "cafingerprint: ${cafingerprint}" >> api-keys.cfg cat ${top_srcdir}/test/catlfish-test.cfg.in api-keys.cfg > ${top_srcdir}/test/catlfish-test.cfg +openssl dgst -sha256 -sign ${top_srcdir}/test/logadminkey-private.pem -out ${top_srcdir}/test/catlfish-test.cfg.sig ${top_srcdir}/test/catlfish-test.cfg for machine in ${MACHINES}; do \ ${top_srcdir}/tools/compileconfig.py --config ${top_srcdir}/test/catlfish-test.cfg --localconfig ${top_srcdir}/test/catlfish-test-local-${machine}.cfg mkdir -p machine/machine-${machine}/db diff --git a/tools/compileconfig.py b/tools/compileconfig.py index 0ee3fab..b5e5053 100755 --- a/tools/compileconfig.py +++ b/tools/compileconfig.py @@ -5,7 +5,7 @@ import argparse import sys -import yaml +import readconfig import re import base64 @@ -405,7 +405,6 @@ def gen_testmakefile(config, testmakefile, machines, shellvars=False): configfile.close() - def main(): parser = argparse.ArgumentParser(description="") parser.add_argument('--config', help="System configuration", required=True) @@ -415,13 +414,16 @@ def main(): parser.add_argument("--machines", type=int, metavar="n", help="Number of machines") args = parser.parse_args() - config = yaml.load(open(args.config)) if args.testmakefile and args.machines: + config = readconfig.read_config(args.config) gen_testmakefile(config, args.testmakefile, args.machines) elif args.testshellvars and args.machines: + config = readconfig.read_config(args.config) gen_testmakefile(config, args.testshellvars, args.machines, shellvars=True) elif args.localconfig: - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) + config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) + localnodes = localconfig["localnodes"] for localnode in localnodes: gen_config(localnode, config, localconfig) diff --git a/tools/loginfo.py b/tools/loginfo.py index c61ad1b..1537c5e 100755 --- a/tools/loginfo.py +++ b/tools/loginfo.py @@ -6,7 +6,7 @@ import sys import argparse -import yaml +import readconfig from certtools import create_ssl_context, get_sth def main(): @@ -26,8 +26,7 @@ def main(): required=True) parser.add_argument('baseurl', help="Log base URL") args = parser.parse_args() - #config = yaml.load(open(args.config)) - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) paths = localconfig["paths"] create_ssl_context(cafile=paths["https_cacertfile"]) diff --git a/tools/merge_sth.py b/tools/merge_sth.py index 6b1bb60..2bc19dd 100755 --- a/tools/merge_sth.py +++ b/tools/merge_sth.py @@ -12,6 +12,7 @@ import json import urllib2 import time import requests +import base64 from base64 import b64encode from mergetools import parse_args, get_nfetched, hexencode, hexdecode, \ get_logorder, get_sth @@ -31,7 +32,7 @@ def merge_sth(args, config, localconfig): sthfile = mergedb + "/sth" logorderfile = mergedb + "/logorder" currentsizefile = mergedb + "/fetched" - logpublickey = get_public_key_from_file(paths["logpublickey"]) + logpublickey = base64.decodestring(config["logpublickey"]) backupquorum = config.get("backup-quorum-size", 0) assert backupquorum <= len(mergenodes) - 1 create_ssl_context(cafile=paths["https_cacertfile"]) diff --git a/tools/mergetools.py b/tools/mergetools.py index f49e789..0afec24 100644 --- a/tools/mergetools.py +++ b/tools/mergetools.py @@ -7,7 +7,7 @@ import hashlib import sys import struct import json -import yaml +import readconfig import argparse import requests try: @@ -430,8 +430,8 @@ def parse_args(): help="Print timing information") args = parser.parse_args() - config = yaml.load(open(args.config)) - localconfig = yaml.load(open(args.localconfig)) + localconfig = readconfig.read_config(args.localconfig) + config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) set_api_keys(config) diff --git a/tools/readconfig.py b/tools/readconfig.py new file mode 100644 index 0000000..5079691 --- /dev/null +++ b/tools/readconfig.py @@ -0,0 +1,54 @@ +import io +import ecdsa +import hashlib +import yaml +import base64 +import sys + +class ErrorHandlingDict(dict): + def __init__(self, filename, path): + self._filename = filename + self._path = path + dict.__init__({}) + def __missing__(self, key): + if self._path: + path = ", ".join(self._path) + else: + path = "the top level" + 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_and_read_config(filename, publickey_base64): + rawconfig = open(filename).read() + signature = open(filename + ".sig").read() + + 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 read_config(filename): + return errorhandlify(yaml.load(open(filename), yaml.SafeLoader), filename) diff --git a/tools/storagegc.py b/tools/storagegc.py index 38b5379..6360495 100755 --- a/tools/storagegc.py +++ b/tools/storagegc.py @@ -9,7 +9,7 @@ import urllib import json import base64 import sys -import yaml +import readconfig from certtools import * parser = argparse.ArgumentParser(description='') @@ -17,8 +17,8 @@ parser.add_argument('--config', help="System configuration", required=True) parser.add_argument('--localconfig', help="Local configuration", required=True) args = parser.parse_args() -config = yaml.load(open(args.config)) -localconfig = yaml.load(open(args.localconfig)) +localconfig = readconfig.read_config(args.localconfig) +config = readconfig.verify_and_read_config(args.config, localconfig["logadminkey"]) paths = localconfig["paths"] db_path = paths["db"] @@ -27,6 +27,7 @@ create_ssl_context(cafile=paths.get("public_cacertfile", None)) baseurl = config["baseurl"] sth = get_sth(baseurl) +check_sth_signature(baseurl, sth, base64.decodestring(config["logpublickey"])) def verifyleafhash(leaf_hash): try: -- cgit v1.1