diff options
author | Rob Austein <sra@hactrn.net> | 2015-04-28 15:29:12 -0400 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2015-04-28 15:29:12 -0400 |
commit | 0c8d1d765783bbc09cc1ca63ffdd233f0ce31613 (patch) | |
tree | 65114ff0b424e0eb6aa8862c12c305bf26282fcb /scripts/test-hsmcheck |
First public commit of PKCS #11 implementation.
Diffstat (limited to 'scripts/test-hsmcheck')
-rwxr-xr-x | scripts/test-hsmcheck | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/scripts/test-hsmcheck b/scripts/test-hsmcheck new file mode 100755 index 0000000..b7a5643 --- /dev/null +++ b/scripts/test-hsmcheck @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +""" +Run the OpenDNSSEC libhsm/check/hsmcheck tool with Cryptech PKCS #11, +using DNSpython to verify the DNSSEC data produced by"hsmcheck -s". + +This script knows far too much about the output generated by hsmcheck, +but what were you expecting from an ad hoc test tool that gets its +input by screen scraping the output of another ad hoc test tool? +""" + +# Author: Rob Austein +# Copyright (c) 2015, SUNET +# +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the following +# conditions are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import sys + +from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, FileType as ArgumentFileType +from tempfile import NamedTemporaryFile +from subprocess import check_call, check_output +from xml.etree.ElementTree import ElementTree, Element, SubElement + + +def write_config(): + """ + Write hsmcheck configuration file. + """ + + e = Element("Configuration") + r = SubElement(e, "RepositoryList") + r = SubElement(r, "Repository", name = "default") + SubElement(r, "Module").text = args.driver + SubElement(r, "TokenLabel").text = args.token_label + SubElement(r, "PIN").text = args.pin + ElementTree(e).write(args.write_config) + args.write_config.flush() + + +def hsmcheck(flag): + """ + Run hsmcheck program with appropriate options and verbosity. + """ + + assert flag in "rgsd" + cmd = (args.hsmcheck_binary, "-c", args.write_config.name, "-" + flag) + if args.verbose: + sys.stdout.write("Running: %s\n" % " ".join(cmd)) + if flag == "s": + text = check_output(cmd) + sys.stdout.write(text) + if not args.no_dnssec: + check_dnssec(text) + else: + check_call(cmd) + + +def check_dnssec(text): + """ + Use DNSPython to attempt DNSSEC validation on "hsmcheck -s" output. + + This requires the DNSPython toolkit, which in turn requires + PyCrypto; ECDSA support (not yet tested) requires a third package. + On Debian-family Linux, you can install these with: + + sudo apt-get install python-dnspython python-crypto python-ecdsa + + Equivalent packages exist for other platforms. + """ + + try: + from dns.exception import DNSException + import dns.dnssec + import dns.rrset + import Crypto.PublicKey.RSA + #import ecdsa.ecdsa + except ImportError: + sys.exit("Problem importing DNSPython or supporting crypto packages, are they installed?") + + wired_ttl = "3600" + wired_rdclass = "IN" + + rrs = {} + + for line in text.splitlines(): + + try: + name, ttl, rdclass, rdtype, rdata = line.split(None, 4) + except ValueError: + continue + + if ttl != wired_ttl or rdclass != wired_rdclass: + continue + + try: + rrs[name, rdtype].append(rdata) + except KeyError: + rrs[name, rdtype] = [rdata] + + # Done parsing. We expect to have seen an A RRset, an RRSIG of that + # A RRset, and the DNSKEY that we'll need to verify the RRSIG. + + if len(rrs) != 3: + sys.exit("Expected two RRsets and an RRSIG, got %r" % rrs) + + rrs = dict((rdtype, dns.rrset.from_text_list(name, int(wired_ttl), wired_rdclass, rdtype, rrs[name, rdtype])) + for name, rdtype in rrs) + + try: + dns.dnssec.validate(rrs["A"], rrs["RRSIG"], { rrs["DNSKEY"].name : rrs["DNSKEY"] }) + except DNSException, e: + sys.exit("DNSSEC verification failed: %s" % e) + + sys.stdout.write("\nDNSSEC verification successful!\n\n") + + +# Main program. + +try: + default_config = NamedTemporaryFile() + default_hsmcheck = os.getenv("HSMCHECK", "hsmcheck") + default_driver = os.getenv("PKCS11_DRIVER", + os.path.realpath(os.path.join(os.path.dirname(sys.argv[0]), "..", "libpkcs11.so"))) + + parser = ArgumentParser(description = __doc__, formatter_class = ArgumentDefaultsHelpFormatter) + one_of = parser.add_mutually_exclusive_group() + one_of.add_argument("-a", "--all", "--rgsd", const = "rgsd", dest = "test", action = "store_const", help = "run all tests") + one_of.add_argument("-r", "--random", const = "r", dest = "test", action = "store_const", help = "just test random numbers") + one_of.add_argument("-g", "--generate", const = "g", dest = "test", action = "store_const", help = "just test key generation") + one_of.add_argument("-s", "--sign", const = "s", dest = "test", action = "store_const", help = "just test DNSSEC-signature") + one_of.add_argument("-d", "--delete", const = "d", dest = "test", action = "store_const", help = "just delete key") + parser.add_argument("-b", "--hsmcheck-binary", default = default_hsmcheck, help = "location of hsmcheck program") + parser.add_argument("-p", "--pin", default = "12345", help = "HSM PIN to use for tests") + parser.add_argument("-t", "--token-label", default = "Cryptech Token", help = "PKCS #11 label of Cryptech token") + parser.add_argument("-n", "--no-dnssec", action = "store_true", help = "do not attempt DNSSEC validation") + parser.add_argument("-v", "--verbose", action = "store_true", help = "bark more") + parser.add_argument("-D", "--driver", default = default_driver, help = "location of PKCS #11 driver") + parser.add_argument("-w", "--write-config", default = default_config, help = "write generated configuration to this file", + type = ArgumentFileType("w")) + parser.add_argument("--debug", action = "store_true", help = "debug this script") + parser.set_defaults(test = "rgsd") + args = parser.parse_args() + + try: + write_config() + for flag in args.test: + hsmcheck(flag) + + except Exception as e: + if args.debug: + raise + sys.exit("Failed: %s" % e) + +finally: + default_config.close() + |