From 0c8d1d765783bbc09cc1ca63ffdd233f0ce31613 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 28 Apr 2015 15:29:12 -0400 Subject: First public commit of PKCS #11 implementation. --- scripts/build-attributes | 403 ++++++++++++++++++++++++++++++++++++++ scripts/convert-schema.sed | 66 +++++++ scripts/format-attribute-comments | 85 ++++++++ scripts/test-hsmcheck | 180 +++++++++++++++++ 4 files changed, 734 insertions(+) create mode 100755 scripts/build-attributes create mode 100644 scripts/convert-schema.sed create mode 100755 scripts/format-attribute-comments create mode 100755 scripts/test-hsmcheck (limited to 'scripts') diff --git a/scripts/build-attributes b/scripts/build-attributes new file mode 100755 index 0000000..891bdb6 --- /dev/null +++ b/scripts/build-attributes @@ -0,0 +1,403 @@ +#!/usr/bin/env python + +""" +Generate a C header file based on a YAML description of PKCS #11 +attributes. See comments in attributes.yaml for details. +""" + +# 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. + +# This requires a third-party YAML parser. On Debian-family Linux, +# you can install this with: +# +# sudo apt-get install python-yaml + +import os +import sys +import yaml +import argparse + + +def define_flags(flag_names): + """ + Flag definitions. Called later, here at front of program just to + make them easier to find. + """ + + flag_names.create("DEFAULT_VALUE", "Value field contains default") + flag_names.footnote( 1, "REQUIRED_BY_CREATEOBJECT") + flag_names.footnote( 2, "FORBIDDEN_BY_CREATEOBJECT") + flag_names.footnote( 3, "REQUIRED_BY_GENERATE") + flag_names.footnote( 4, "FORBIDDEN_BY_GENERATE") + flag_names.footnote( 5, "REQUIRED_BY_UNWRAP") + flag_names.footnote( 6, "FORBIDDEN_BY_UNWRAP") + flag_names.footnote( 7, "SENSITIVE") + flag_names.footnote( 8, "PERHAPS_MODIFIABLE") + flag_names.footnote( 9, "DEFAULT_IS_TOKEN_SPECIFIC") + flag_names.footnote(10, "ONLY_SO_USER_CAN_SET") + flag_names.footnote(11, "LATCHES_WHEN_TRUE") + flag_names.footnote(12, "LATCHES_WHEN_FALSE") + + +class PKCS11ParseError(Exception): + "Failure parsing PCKS #11 object definitions from YAML data." + + +def write_lines(*lines, **d): + """ + Utility to simplify writing formatted text to the output stream. + """ + + for line in lines: + args.output_file.write((line % d) + "\n") + + +class Flags(object): + """ + Descriptor flag database. + + Many of these are derived from PKCS #11 Table 15 footnotes + """ + + prefix = "P11_DESCRIPTOR_" # Prefix string for all descriptor flags + + def __init__(self): + self.names = [] + self.notes = {} + self.width = 0 + + def create(self, name, comment = None): + """ + Create a descriptor flag. + """ + + assert len(self.names) < 32 + name = self.prefix + name + self.names.append((name, comment)) + if len(name) > self.width: + self.width = len(name) + + def footnote(self, number, name): + """ + Create a descriptor flag for a PKCS #11 table 15 footnote. + """ + + assert number not in self.notes + self.create(name, "Section 10.2 table 15 footnote #%2d" % number) + self.notes[number] = self.prefix + name + + def write(self): + """ + Generate the flags, assigning bit positions as we go. + """ + + assert len(self.names) < 32 + self.width = (((self.width + 4) >> 2) << 2) - 1 + bit = 1 + for name, comment in self.names: + format = "#define %(name)s 0x%(bit)08x" + if comment is not None: + format += " /* %(comment)s */" + write_lines(format, bit = bit, comment = comment, name = "%-*s" % (self.width, name)) + bit <<= 1 + + +class AttributeNumbers(dict): + """ + Attribute names and numbers scraped (yuck) from pkcs11t.h. + """ + + def __init__(self, filename): + with open(filename, "r") as f: + for line in f: + word = line.split() + if len(word) <= 2 or word[0] != "#define" or not word[1].startswith("CKA_"): + continue + if word[2] in self: + continue + if word[2].startswith("(CKF_ARRAY_ATTRIBUTE|"): + word[2] = word[2].translate(None, "()").split("|")[1] + self[word[1]] = int(word[2], 16) + + +class Attribute(object): + """ + Definition of one attribute. + """ + + def __init__(self, name, type = None, footnotes = None, default = None, value = None, unimplemented = False): + assert value is None or default is None + self.name = name + self.type = type + self.footnotes = footnotes + self.default = self.convert_integers(default) + self.value = self.convert_integers(value) + self.unimplemented = unimplemented + + @staticmethod + def convert_integers(val): + """ + Convert a non-negative integer initialization value into a byte array. + """ + + if not isinstance(val, (int, long)): + return val + if val < 0: + raise ValueError("Negative integers not legal here: %s" % val) + bytes = [] + while val > 0: + bytes.insert(0, val & 0xFF) + val >>= 8 + return bytes or [0] + + def inherit(self, other): + """ + Merge values from paraent attribute definition, if any. + """ + + for k in ("type", "footnotes", "default", "value"): + if getattr(self, k) is None: + setattr(self, k, getattr(other, k)) + self.unimplemented = self.unimplemented or other.unimplemented + + def format_flags(self): + """ + Generate the descriptor flags field. + """ + + flags = [] + if self.footnotes: + flags.extend(flag_names.notes[f] for f in self.footnotes) + if self.value is None and self.default is not None: + flags.append("P11_DESCRIPTOR_DEFAULT_VALUE") + flags = " | ".join(flags) + return flags or "0" + + def format_size(self): + """ + Generate the descriptor size field. + """ + + if isinstance(self.type, str) and self.type.startswith("CK_"): + return "sizeof(%s)" % self.type + elif self.type in ("rfc2279string", "biginteger", "bytearray"): + return "0" + else: + raise PKCS11ParseError("Unknown meta-type %r" % self.type) + + def format_length(self): + """ + Generate the descriptor length field. + """ + + value = self.value or self.default + if isinstance(value, list): + return "sizeof(const_0x%s)" % "".join("%02x" % v for v in value) + elif value and isinstance(self.type, str) and self.type.startswith("CK_"): + return "sizeof(%s)" % self.type + else: + return "0" + + def format_value(self): + """ + Generate the descriptor value field. + """ + + value = self.value or self.default + if not value: + return "NULL_PTR" + elif isinstance(value, list): + return "const_0x" + "".join("%02x" % v for v in value) + else: + return "&const_" + value + + def format_constant(self, constants): + """ + Generate constant initializer values. These are merged so that we + only end up declaring one copy of each initializer value no matter + how many attributes use it. + """ + + value = self.value or self.default + if not self.unimplemented and value: + if isinstance(value, list): + constants.add("static const CK_BYTE const_%s[] = { %s };" % ( + "0x" + "".join("%02x" % v for v in value), + ", ".join("0x%02x" % v for v in value))) + else: + constants.add("static const %s const_%s = %s;" % (self.type, value, value)) + + def generate(self): + """ + Generate the descriptor line for this attribute. + """ + + if not self.unimplemented: + args.output_file.write(" { %s, %s, %s, %s, %s },\n" % ( + self.name, self.format_size(), self.format_length(), self.format_value(), self.format_flags())) + + +class Class(object): + """ + A PKCS #11 class. + """ + + def __init__(self, db, name, superclass = None, concrete = False, **attrs): + assert all(a.startswith("CKA_") for a in attrs), "Non-attribute: %r" % [a for a in attrs if not a.startswith("CKA_")] + self.attributes = dict((k, Attribute(k, **v)) for k, v in attrs.iteritems()) + self.db = db + self.name = name + self.superclass = superclass + self.concrete = concrete + + def inherit(self, other): + """ + Inherit attributes from parent type. + """ + + for k, v in other.attributes.iteritems(): + if k not in self.attributes: + self.attributes[k] = v + else: + self.attributes[k].inherit(v) + + def collect_constants(self, constants): + """ + Collect initialization constants for all attributes. + """ + + if self.concrete: + for a in self.attributes.itervalues(): + a.format_constant(constants) + + def generate(self): + """ + Generate a descriptor for this type. + """ + + if self.concrete: + + write_lines("", + "static const p11_attribute_descriptor_t p11_attribute_descriptor_%(name)s[] = {", + name = self.name) + + for a in sorted(self.attributes, key = lambda x: attribute_numbers[x]): + self.attributes[a].generate() + + write_lines("};", + "", + "static const p11_descriptor_t p11_descriptor_%(name)s = {", + " p11_attribute_descriptor_%(name)s,", + " sizeof(p11_attribute_descriptor_%(name)s)/sizeof(p11_attribute_descriptor_t)", + "};", + name = self.name) + + def keyclassmap(self): + """ + Generate a keyclass map entry if this is a concrete key type. + """ + + if self.concrete and all(k in self.attributes and self.attributes[k].value for k in ("CKA_CLASS", "CKA_KEY_TYPE")): + write_lines(" { %s, %s, &p11_descriptor_%s }," % ( + self.attributes["CKA_CLASS"].value, self.attributes["CKA_KEY_TYPE"].value, self.name)) + + +class DB(object): + """ + Object type database parsed from YAML + """ + + def __init__(self, y): + self.ordered = [Class(self, **y) for y in y] + self.named = dict((c.name, c) for c in self.ordered) + for c in self.ordered: + if c.superclass is not None: + c.inherit(self.named[c.superclass]) + + def generate(self): + """ + Generate output for everything in the database. + """ + + constants = set() + for c in self.ordered: + c.collect_constants(constants) + for constant in sorted(constants): + write_lines(constant) + for c in self.ordered: + c.generate() + write_lines("", + "static const p11_descriptor_keyclass_map_t p11_descriptor_keyclass_map[] = {") + for c in self.ordered: + c.keyclassmap() + write_lines("};") + +# Main program + +parser = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("--pkcs11t-file", help = "Alternate location for pkcs11t.h", default = "pkcs11t.h") +parser.add_argument("yaml_file", help = "Input YAML file", nargs = "?", type = argparse.FileType("r"), default = sys.stdin) +parser.add_argument("output_file", help = "Output .h file", nargs = "?", type = argparse.FileType("w"), default = sys.stdout) +args = parser.parse_args() + +attribute_numbers = AttributeNumbers(args.pkcs11t_file) + +db = DB(yaml.load(args.yaml_file)) + +args.output_file.write('''\ +/* + * This file was generated automatically from %(input)s by %(script)s. Do not edit this file directly. + */ + +typedef struct { + CK_ATTRIBUTE_TYPE type; + CK_ULONG size; /* Size in bytes if this is a fixed-length attribute */ + CK_ULONG length; /* Length in bytes of the object to which value points */ + const void *value; /* Default or constant depending on P11_DESCRIPTOR_DEFAULT_VALUE */ + unsigned long flags; /* (NULL value with P11_DESCRIPTOR_DEFAULT_VALUE means zero length default */ +} p11_attribute_descriptor_t; + +typedef struct { + const p11_attribute_descriptor_t *attributes; + CK_ULONG n_attributes; +} p11_descriptor_t; + +typedef struct { + CK_OBJECT_CLASS object_class; + CK_KEY_TYPE key_type; + const p11_descriptor_t *descriptor; +} p11_descriptor_keyclass_map_t; + +''' % dict(script = os.path.basename(sys.argv[0]), input = args.yaml_file.name)) + +flag_names = Flags() +define_flags(flag_names) +flag_names.write() +write_lines("") +db.generate() diff --git a/scripts/convert-schema.sed b/scripts/convert-schema.sed new file mode 100644 index 0000000..55aaadc --- /dev/null +++ b/scripts/convert-schema.sed @@ -0,0 +1,66 @@ +# Generate schema.h from schema.sql. +# +# If this script gets any more complicated, it should probably be +# recoded in Python and have done. +# +# 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. + + +# Add header. Note that both newlines and leading spaces need to be +# quoted with backslashes, be careful.... +1i\ + /*\ +\ * Automatically generated from schema.sql, edit that file instead of this one.\ +\ */\ +\ + +# Debugging hack: ordinarily we keep all the per-session stuff in the +# "temp" database, but debugging is easier when we let it all go to +# disk. Uncomment these lines to remove all the "TEMPORARY" and +# "temp." qualifiers. +#s/ TEMPORARY / /g +#s/ temp[.]/ /g + +# Delete comment lines, trailing whitespace, and blank lines. +/^[ ]*--/d +s/[ ]*$// +/^$/d + +# Quote backslashes and doublequotes, if any. +s/\\/\\\\/g +s/"/\\"/g + +# Quote each line of text. Literal transcription would be: +# +# s/^.*$/"&\\n"/ +# +# but SQL doesn't need the line breaks, so we can use +# whitespace to generate something a bit more readable. +# +s/^.*$/" &"/ diff --git a/scripts/format-attribute-comments b/scripts/format-attribute-comments new file mode 100755 index 0000000..3c13bba --- /dev/null +++ b/scripts/format-attribute-comments @@ -0,0 +1,85 @@ +#!/bin/sh - +# +# Script to extract tables from the PKCS #11 specification and format +# them as YAML comment blocks. +# +# This isn't even half-assed, more like quarter-assed. If I thought +# we'd be using it a lot I'd rewrite it in Python. +# +# 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. + +url=http://www.cryptsoft.com/pkcs11doc/download/pkcs11doc-v230.tgz + +tar=${url##*/} + +test -r $tar || +wget $url || +exit + +tar -tf $tar | + +awk ' + /group__SEC__(9|11)__.*\.html/ { + + n = split($0, a, "[/.]"); + title = a[n-1]; + + n = split($0, a, /__/); + s1 = a[3]; + s2 = (a[4] ~ /^[0-9]+$/) ? a[4] : 0; + s3 = (a[5] ~ /^[0-9]+$/) ? a[5] : 0; + idx = sprintf("%04d%04d%04d", s1, s2, s3); + + print idx, $0, title; + } +' | + +sort -n | + +while read idx fn title +do + + tar -xOf $tar $fn | + + w3m -dump -O us-ascii -T text/html | + + awk -v title=$title ' + BEGIN { + print ""; + print "###"; + print "#", title; + print "###"; + print ""; + } + /^[|+]/ { + print "#", $0; + } + ' + +done 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() + -- cgit v1.2.3