aboutsummaryrefslogtreecommitdiff
path: root/scripts/test-hsmcheck
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/test-hsmcheck')
-rwxr-xr-xscripts/test-hsmcheck180
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()
+