aboutsummaryrefslogtreecommitdiff
path: root/tests
AgeCommit message (Expand)Author
2015-06-04Disable HMAC-SHA-384 tests as neither my implementation nor PyCryptoRob Austein
2015-06-04HMAC implementation and test vectors.Rob Austein
2015-06-04Refactor hash code prior to adding HMAC (which we need for PBKDF2).Rob Austein
2015-06-03ModExp now working!Rob Austein
2015-06-01Add padding options to test workaround for current ModExp bugs.Rob Austein
2015-05-28More fun with RSA test cases, still not working.Rob Austein
2015-05-27First pass at RSA tests.Rob Austein
2015-05-25Cleanup: names of *_core_present() functions, Makefile.Rob Austein
2015-05-25Doh, skip tests when we know core isn't present.Rob Austein
2015-05-25Add missing truncated SHA-512 cases.Rob Austein
2015-05-24Cleanup.Rob Austein
2015-05-24Debug hash-testing code.Rob Austein
2015-05-24First pass on hash test code.Rob Austein
2015-05-24AES key wrap now working with AES core.Rob Austein
2015-05-24Typing "!" when one meant "~" has interesting effects when bit masking.Rob Austein
2015-05-23Guess it might help to write the config value to the AES core afterRob Austein
2015-05-21Add test cases for 128-bit and 256-bit KEKs.Rob Austein
2015-05-21Add test harness: no useful tests yet, just the framework.Rob Austein
66'>166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
#!/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, NORDUnet A/S
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# - Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# - 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.
#
# - Neither the name of the NORDUnet nor the names of its contributors may
#   be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# 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
# HOLDER 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()