Beispiel #1
0
    def test_sort_action_groups(self):
        """Test sort_action_groups sorts action groups by custom title order. """
        # Create custom order for titles (default is asserted in setUp)
        custom_order = ["optional arguments", "positional arguments"]
        sort_action_groups(self.parser, title_order=custom_order)
        # Assert successful re-ordering
        self.assertListEqual(
            [group.title for group in self.parser._action_groups],
            custom_order)

        # Add custom group to parser that exists in most in-toto command line tools
        self.parser.add_argument_group("required named arguments")

        # Test default custom order of action groups titles (which are title-cased)
        title_case_action_groups(self.parser)
        sort_action_groups(self.parser)
        default_custom_order = [
            "Required Named Arguments", "Positional Arguments",
            "Optional Arguments"
        ]

        # Assert successful(title-casing) re-ordering
        self.assertListEqual(
            [group.title for group in self.parser._action_groups],
            default_custom_order)
Beispiel #2
0
  def test_title_case_action_groups(self):
    """Test title_case_action_groups title cases action group titles. """
    # Make titles title-case (default is asserted in setUp)
    title_case_action_groups(self.parser)

    # Assert successful title-casing
    self.assertListEqual([group.title for group in self.parser._action_groups],
        ["Positional Arguments", OPTS_TITLE])
Beispiel #3
0
def create_parser():
    """Parse arguments and call in_toto_mock. """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
in-toto-mock is a variant of 'in-toto-run' that can be used to create unsigned
link metadata, using defaults for many of the 'in-toto-run' arguments.
in-toto-mock verbosely executes the passed command, records all files in the
current working directory as materials and products, and generates a link file
under '<name>.link'.

This is useful for trying out how to generate a link without the need for a
key, or knowledge about all 'in-toto-run' arguments. It can also be used to
quickly generate link metadata, inspect it and sign it retroactively.

""")

    parser.usage = "%(prog)s [-h] --name <name> -- <command> [args]"

    parser.epilog = """EXAMPLE USAGE

Generate unsigned link metadata 'foo.link' for the activity of creating file
'bar', inspect it, and sign it with 'mykey'

  # Generate unsigned link
  {prog} --name foo -- touch bar
  # Inspect and/or update unsigned link metadata
  vi foo.link
  # Sign the link, attesting to its validity, and write it to
  # 'foo.<mykey keyid prefix>.link'.
  in-toto-sign -k mykey -f foo.link

""".format(prog=parser.prog)

    named_args = parser.add_argument_group("required named arguments")

    # FIXME: Do we limit the allowed characters for the name?
    named_args.add_argument(
        "-n",
        "--name",
        type=str,
        required=True,
        metavar="<name>",
        help=(
            "name for the resulting link metadata file, which is written to"
            " '<name>.link'. It is also used to associate the link with a step"
            " defined in an in-toto layout."))

    # FIXME: This is not yet ideal.
    # What should we do with tokens like > or ; ?
    parser.add_argument(
        "link_cmd",
        nargs="+",
        metavar="<command>",
        help=("command to be executed. It is separated from named and optional"
              " arguments by a double dash '--'."))

    parser.add_argument('--version',
                        action='version',
                        version='{} {}'.format(parser.prog, __version__))

    title_case_action_groups(parser)
    sort_action_groups(parser)

    return parser
Beispiel #4
0
def create_parser():
    """Create and return configured ArgumentParser instance. """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
in-toto-verify is the main verification tool of the suite, and it is used to
verify that the software supply chain of the delivered product was carried out
as defined in the passed in-toto supply chain layout. Evidence for supply chain
steps must be available in the form of link metadata files named
'<step name>.<functionary keyid prefix>.link'.

Both 'in-toto-run' and 'in-toto-record' generate link metadata named in this
manner. If you require special handling of the in-toto link metadata files,
please take a look at the library api to modify this behavior.

The verification includes the following checks:
  * the layout is signed with the passed keys,
  * the layout has not expired,
  * a threshold of link metadata files exists for each step of the layout,
  * link files are signed by the authorized functionaries,
  * the materials and products for each step, as reported by the corresponding
    link files, adhere to the artifact rules specified by the step.

Additionally, inspection commands defined in the layout are executed
sequentially, followed by processing the inspections' artifact rules.

If the layout includes sublayouts, the verification routine will recurse into a
subdirectory named '<step name>.<keyid prefix>', where all the links relevant
to that sublayout must exist. The sublayout itself must be in the same
directory as the other links of the superlayout. (i.e. '<step name>.<keyid
prefix>.link')

The command returns 2 if it is called with wrong arguments, 1 if in-toto
verification fails and 0 if verification passes. """)

    parser.usage = "%(prog)s <named arguments> [optional arguments]"

    parser.epilog = """EXAMPLE USAGE

Verify supply chain in 'root.layout', signed with private part of
'key_file.pub'.

  {prog} --layout root.layout --layout-keys key_file.pub


Verify supply chain as above but load links corresponding to steps of
'root.layout' from 'link_dir'.

  {prog} --layout root.layout --layout-keys key_file.pub \\
      --link-dir link_dir


Verify supply chain in 'root.layout', signed with GPG key '...7E0C8A17',
for which the public part can be found in the GPG keyring at '~/.gnupg'.

  {prog} --layout root.layout \\
      --gpg 8465A1E2E0FB2B40ADB2478E18FB3F537E0C8A17 \\
      --gpg-home ~/.gnupg


""".format(prog=parser.prog)

    named_args = parser.add_argument_group("required named arguments")

    named_args.add_argument(
        "-l",
        "--layout",
        type=str,
        required=True,
        metavar="<path>",
        help=("path to root layout specifying the software supply chain to be"
              " verified."))

    named_args.add_argument(
        "-k",
        "--layout-keys",
        type=str,
        metavar="<path>",
        nargs="+",
        help=
        ("paths to public key files used to verify the passed root layout's"
         " signatures. See '--key-types' for available formats. Passing at least"
         " one key using '--layout-keys' and/or '--gpg' is required. For each"
         " passed key the layout must carry a valid signature."))

    parser.add_argument(
        "-t",
        "--key-types",
        dest="key_types",
        type=str,
        choices=in_toto.util.SUPPORTED_KEY_TYPES,
        nargs="+",
        help=
        ("types of keys specified by the '--layout-keys' option. '{rsa}' keys are"
         " expected in a 'PEM' format and '{ed25519}' in a custom"
         " 'securesystemslib/json' format. If multiple keys are passed via"
         " '--layout-keys' the same amount of key types must be passed. Key"
         " types are then associated with keys by index. If '--key-types' is"
         " omitted, the default of '{rsa}' is used for all keys.".format(
             rsa=in_toto.util.KEY_TYPE_RSA,
             ed25519=in_toto.util.KEY_TYPE_ED25519)))

    named_args.add_argument(
        "-g",
        "--gpg",
        nargs="+",
        metavar="<id>",
        help=
        ("GPG keyid, identifying a public key in the GPG keyring used to verify"
         " the passed root layout's signatures."
         " Passing at least one key using '--layout-keys' and/or '--gpg' is"
         " required. For each passed key the layout must carry a valid"
         " signature."))

    parser.add_argument(
        "--link-dir",
        dest="link_dir",
        type=str,
        metavar="<path>",
        default=".",
        help=(
            "path to directory from which link metadata files for steps defined"
            " in the root layout should be loaded. If not passed, links are"
            " loaded from the current working directory."))

    parser.add_argument(*GPG_HOME_ARGS, **GPG_HOME_KWARGS)

    verbosity_args = parser.add_mutually_exclusive_group(required=False)
    verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
    verbosity_args.add_argument(*QUIET_ARGS, **QUIET_KWARGS)

    parser.add_argument('--version',
                        action='version',
                        version='{} {}'.format(parser.prog, __version__))

    title_case_action_groups(parser)
    sort_action_groups(parser)

    return parser
Beispiel #5
0
def create_parser():
    """Create and return configured ArgumentParser instance. """

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
in-toto-record creates a signed link metadata file in two steps, in order to
provide evidence for supply chain steps that cannot be carried out by a single
command (for which 'in-toto-run' should be used). It returns a non-zero value
on failure and zero otherwise.""")

    parser.epilog = """EXAMPLE USAGE

Create link metadata file in two commands, signing it with the private key
loaded from 'key_file', recording all files in the CWD as materials (on
start), and as products (on stop).

  {prog} start -n edit-files -k path/to/key_file -m .
  {prog} stop -n edit-files -k path/to/key_file -p .


Create link metadata file signed with the default GPG key from the default
GPG home directory and record a file named 'foo' as material and product.

  {prog} start -n edit-foo --gpg -m path/to/foo
  {prog} stop -n edit-foo --gpg -p path/to/foo


Create link metadata file signed with the private key loaded from 'key_file',
record all files in the CWD as material and product, and dump finished link
file to the target directory (on stop).

  {prog} start -n edit-files -k path/to/key_file -m .
  {prog} stop -d path/to/target/dir -n edit-files -k path/to/key_file -p .

""".format(prog=parser.prog)

    # The subparsers inherit the arguments from the parent parser
    parent_parser = argparse.ArgumentParser(add_help=False)
    subparsers = parser.add_subparsers(dest="command")

    # Workaround to make subcommands mandatory in Python>=3.3
    # https://bugs.python.org/issue9253#msg186387
    subparsers.required = True

    parent_named_args = parent_parser.add_argument_group(
        "required named arguments")

    # FIXME: Do we limit the allowed characters for the name?
    parent_named_args.add_argument(
        "-n",
        "--step-name",
        type=str,
        required=True,
        metavar="<name>",
        help=
        ("name for the resulting link metadata file. It is also used to associate"
         " the link with a step defined in an in-toto layout."))

    parent_named_args.add_argument(*KEY_ARGS, **KEY_KWARGS)
    parent_parser.add_argument(*KEY_TYPE_ARGS, **KEY_TYPE_KWARGS)
    parent_parser.add_argument(*KEY_PASSWORD_ARGS, **KEY_PASSWORD_KWARGS)

    parent_named_args.add_argument(*GPG_ARGS, **GPG_KWARGS)
    parent_parser.add_argument(*GPG_HOME_ARGS, **GPG_HOME_KWARGS)

    parent_parser.add_argument(*EXCLUDE_ARGS, **EXCLUDE_KWARGS)
    parent_parser.add_argument(*BASE_PATH_ARGS, **BASE_PATH_KWARGS)
    parent_parser.add_argument(*LSTRIP_PATHS_ARGS, **LSTRIP_PATHS_KWARGS)

    verbosity_args = parent_parser.add_mutually_exclusive_group(required=False)
    verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
    verbosity_args.add_argument(*QUIET_ARGS, **QUIET_KWARGS)

    subparser_start = subparsers.add_parser(
        "start",
        parents=[parent_parser],
        help=(
            "creates a preliminary link file recording the paths and hashes of"
            " the passed materials and signs it with the passed functionary's"
            " key. The resulting link file is stored as"
            " '.<name>.<keyid prefix>.link-unfinished'."))

    subparser_stop = subparsers.add_parser(
        "stop",
        parents=[parent_parser],
        help=
        ("expects preliminary link file '.<name>.<keyid prefix>.link-unfinished'"
         " in the CWD, signed by the passed functionary's key. If found, it"
         " records and adds the paths and hashes of the passed products to the"
         " link metadata file, updates the signature and renames the file to"
         " '<name>.<keyid prefix>.link'."))

    subparser_start.add_argument(
        "-m",
        "--materials",
        type=str,
        required=False,
        nargs='+',
        metavar="<path>",
        help=
        ("paths to files or directories, whose paths and hashes are stored in the"
         " resulting link metadata's material section when running the 'start'"
         " subcommand. Symlinks to files are followed."))

    subparser_stop.add_argument(
        "-p",
        "--products",
        type=str,
        required=False,
        nargs='+',
        metavar="<path>",
        help=
        ("paths to files or directories, whose paths and hashes are stored in the"
         " resulting link metadata's product section when running the 'stop'"
         " subcommand. Symlinks to files are followed."))

    subparser_stop.add_argument(*METADATA_DIRECTORY_ARGS,
                                **METADATA_DIRECTORY_KWARGS)

    parser.add_argument('--version',
                        action='version',
                        version='{} {}'.format(parser.prog, __version__))

    for _parser, _order in [(parser,
                             ["Positional Arguments", "Optional Arguments"]),
                            (subparser_start, None), (subparser_stop, None)]:
        title_case_action_groups(_parser)
        sort_action_groups(_parser, _order)

    return parser
Beispiel #6
0
def create_parser():
    """
  <Purpose>
    A function which parses the user supplied arguments.

  <Arguments>
    None

  <Exceptions>
    None

  <Returns>
    Parsed arguments (args object)
  """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=
        "in-toto-keygen is a tool to generate, optionally encrypt, and"
        " write cryptographic keys to disk. These keys may be used"
        " with other in-toto tooling to e.g. sign or verify link or"
        " layout metadata.")

    parser.epilog = """EXAMPLE USAGE

Generate RSA key pair of size 2048 bits, prompt for a password to encrypt
the private key, and write 'alice' (private encrypted) and 'alice.pub' (public)
as PEM-formatted key files to the current working directory.

  in-toto-keygen -p -t rsa -b 2048 alice


Generate unencrypted ed25519 key pair and write 'bob' (private) and 'bob.pub'
(public) as securesystemslib/json-formatted key files to the current working
directory.

  in-toto-keygen -t ed25519 bob


"""

    parser.add_argument("-p",
                        "--prompt",
                        action="store_true",
                        help="prompts for a password used to encrypt the"
                        " private key before storing it")

    parser.add_argument("-t",
                        "--type",
                        type=str,
                        choices=SUPPORTED_KEY_TYPES,
                        default=KEY_TYPE_RSA,
                        help="type of the key to be generated. '{rsa}'"
                        " keys are written in a 'PEM' format and"
                        " '{ed25519}' in a custom 'securesystemslib/json'"
                        " format. Default is '{rsa}'.".format(
                            rsa=KEY_TYPE_RSA, ed25519=KEY_TYPE_ED25519))

    parser.add_argument("name",
                        type=str,
                        metavar="<filename>",
                        help="filename for the resulting key files, which"
                        " are written to '<filename>' (private key) and"
                        " '<filename>.pub' (public key).")

    parser.add_argument("-b",
                        "--bits",
                        default=3072,
                        type=int,
                        metavar="<bits>",
                        help="key size, or key length, of the RSA key")

    parser.add_argument('--version',
                        action='version',
                        version='{} {}'.format(parser.prog, __version__))

    title_case_action_groups(parser)

    return parser
Beispiel #7
0
def create_parser():
    """Create and return configured ArgumentParser instance. """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
in-toto-sign provides a command line interface to sign in-toto link or layout
metadata or verify their signatures, with options to:

  * replace (default) or add signatures:

    + layout metadata can be signed by multiple keys at once,
    + link metadata can only be signed by one key at a time.

  * write signed metadata to a specified path. If no output path is specified,

    + layout metadata is written to the path of the input file,
    + link metadata is written to '<name>.<keyid prefix>.link'.

  * verify signatures

in-toto-sign is useful to re-sign metadata (e.g. when changing keys), or to
sign unsigned links (e.g. generated with 'in-toto-mock'). For layouts, it is
useful to append signatures in case threshold signing of layouts is necessary.

It returns a non-zero value on failure and zero otherwise.""")

    parser.epilog = """EXAMPLE USAGE

Sign 'unsigned.layout' with two keys and write it to 'root.layout'.

  {prog} -f unsigned.layout -k priv_key1 priv_key2 -o root.layout


Replace signature in link file and write to default filename, i.e.
'package.<priv_key keyid prefix>.link'.

  {prog} -f package.2f89b927.link -k priv_key


Verify layout signed with 3 keys.

  {prog} -f root.layout -k pub_key0 pub_key1 pub_key2 --verify


Sign layout with default gpg key in default gpg keyring.

  {prog} -f root.layout --gpg


Verify layout with a gpg key identified by keyid '...439F3C2'.

  {prog} -f root.layout --verify \\
      --gpg 3BF8135765A07E21BD12BF89A5627F6BF439F3C2

""".format(prog=parser.prog)

    named_args = parser.add_argument_group("required named arguments")

    named_args.add_argument(
        "-f",
        "--file",
        type=str,
        required=True,
        metavar="<path>",
        help=("path to link or layout file to be signed or verified."))

    parser.add_argument(
        "-k",
        "--key",
        nargs="+",
        metavar="<path>",
        help=
        ("paths to key files, used to sign the passed link or layout metadata"
         " or to verify its signatures. See '--key-type' for available formats."
         ))

    parser.add_argument(
        "-t",
        "--key-type",
        dest="key_type",
        type=str,
        choices=util.SUPPORTED_KEY_TYPES,
        nargs="+",
        help=(
            "types of keys specified by the '--key' option. '{rsa}' keys are"
            " expected in a 'PEM' format and '{ed25519}' in a custom"
            " 'securesystemslib/json' format. If multiple keys are passed via"
            " '--key' the same amount of key types must be passed. Key"
            " types are then associated with keys by index. If '--key-type' is"
            " omitted, the default of '{rsa}' is used for all keys.".format(
                rsa=util.KEY_TYPE_RSA, ed25519=util.KEY_TYPE_ED25519)))

    parser.add_argument(
        "-g",
        "--gpg",
        nargs="*",
        metavar="<id>",
        help=
        ("GPG keyids used to sign the passed link or layout metadata or to verify"
         " its signatures. If passed without keyids, the default GPG key is"
         " used."))

    parser.add_argument(*GPG_HOME_ARGS, **GPG_HOME_KWARGS)

    # Only when signing
    parser.add_argument(
        "-o",
        "--output",
        type=str,
        metavar="<path>",
        help=
        ("path to location where the metadata file is stored after signing. If"
         " not passed, layout metadata is written to the path of the input file"
         " and link metadata is written to '<name>.<keyid prefix>.link'"))

    # Only when signing
    parser.add_argument(
        "-a",
        "--append",
        action="store_true",
        help=
        ("add signatures rather than replacing existing signatures. This option"
         " is only availabe for layout metdata."))

    parser.add_argument(
        "--verify",
        action="store_true",
        help="verify signatures of passed link or layout metadata using the"
        " public keys passed via '--key' and/or '--gpg' options.")

    verbosity_args = parser.add_mutually_exclusive_group(required=False)
    verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
    verbosity_args.add_argument(*QUIET_ARGS, **QUIET_KWARGS)

    parser.add_argument('--version',
                        action='version',
                        version='{} {}'.format(parser.prog, __version__))

    title_case_action_groups(parser)
    sort_action_groups(parser)

    return parser
Beispiel #8
0
def create_parser():
  """Create and return configured ArgumentParser instance. """
  parser = argparse.ArgumentParser(
      formatter_class=argparse.RawDescriptionHelpFormatter,
      description="""
in-toto-run is the main command line interface for generating link
metadata while carrying out a supply chain step. To do this, it wraps the
passed command, and attempts to track all relevant information about the
wrapped command's execution. It records paths and hashes of 'materials' (files
before command execution) and 'products' (files after command execution) and
writes them together with other information (executed command, return value,
stdout and stderr) to a link metadata file, which is signed with the passed
key. It returns a non-zero value on failure and zero otherwise.""")

  parser.usage = ("%(prog)s <named arguments> [optional arguments] \\\n\t"
      " -- <command> [args]")

  parser.epilog = """EXAMPLE USAGE

Tag a git repo, storing files in CWD as products, signing the resulting link
file with the private key loaded from 'key_file'.

  {prog} -n tag -p . -k key_file -- git tag v1.0


Create a tarball, storing files in 'project' directory as materials and the
tarball as product, signing the link file with a GPG key '...7E0C8A17'.

  {prog} -n package -m project -p project.tar.gz \\
         -g 8465A1E2E0FB2B40ADB2478E18FB3F537E0C8A17 \\
         -- tar czf project.tar.gz project


Not all supply chain steps require that a command be executed. in-toto can
still generate signed attestations, e.g. for review work. In that case, files
may be marked as materials for the manual review process and the command be
omitted.

  {prog} -n review -k key_file -m document.pdf -x


If an artifact that should be recorded is not in the current working directory
(or one of its subdirectories) it can be located using the base path option.
Note that in this example only the relative path, 'document.pdf' is stored
along with its hash in the resulting link metadata file.

  {prog} -n review -k key_file -m document.pdf \\
         --base-path /my/review/docs/ -x


Similarly, it is possible to pass the full path to the artifact that should
be recorded together with a left-strip path, to only store a relative path,
e.g. 'document.pdf'.

  {prog} -n review -k key_file \\
         -m /tmp/my/review/docs/document.pdf \\
         --lstrip-paths /tmp/my/review/docs/ -x


""".format(prog=parser.prog)


  named_args = parser.add_argument_group("required named arguments")

  # FIXME: Do we limit the allowed characters for the name?
  named_args.add_argument("-n", "--step-name", type=str, required=True,
      metavar="<name>", help=(
      "name for the resulting link metadata file, which is written to"
      " '<name>.<keyid prefix>.link'. It is also used to associate the link"
      " with a step defined in an in-toto layout."))

  parser.add_argument("-m", "--materials", type=str, required=False,
      nargs='+', metavar="<path>", help=(
      "paths to files or directories, for which paths and hashes are stored in"
      " the resulting link metadata before the command is executed. Symlinks"
      " to files are followed."))

  parser.add_argument("-p", "--products", type=str, required=False,
      nargs='+', metavar="<path>", help=(
      "paths to files or directories, for which paths and hashes are stored in"
      " the resulting link metadata after the command is executed. Symlinks to"
      " files are followed."))

  parser.add_argument("-s", "--record-streams", dest="record_streams",
      default=False, action="store_true", help=(
      "duplicate 'stdout' and 'stderr' of the executed command and store the"
      " contents in the resulting link metadata. Do not use with interactive"
      " commands."))

  parser.add_argument("-x", "--no-command", dest="no_command", default=False,
    action="store_true", help=(
    "generate link metadata without executing a command, e.g. for a"
    " signed-off-by step."))

  named_args.add_argument(*KEY_ARGS, **KEY_KWARGS)
  parser.add_argument(*KEY_TYPE_ARGS, **KEY_TYPE_KWARGS)

  named_args.add_argument(*GPG_ARGS, **GPG_KWARGS)
  parser.add_argument(*GPG_HOME_ARGS, **GPG_HOME_KWARGS)

  parser.add_argument(*EXCLUDE_ARGS, **EXCLUDE_KWARGS)
  parser.add_argument(*BASE_PATH_ARGS, **BASE_PATH_KWARGS)
  parser.add_argument(*LSTRIP_PATHS_ARGS, **LSTRIP_PATHS_KWARGS)

  verbosity_args = parser.add_mutually_exclusive_group(required=False)
  verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
  verbosity_args.add_argument(*QUIET_ARGS, **QUIET_KWARGS)


  # FIXME: This is not yet ideal.
  # What should we do with tokens like > or ; ?
  parser.add_argument("link_cmd", nargs="*", metavar="<command>",
      help=(
      "command to be executed. It is separated from named and optional"
      " arguments by a double dash '--'."))

  parser.add_argument('--version', action='version',
                      version='{} {}'.format(parser.prog, __version__))

  title_case_action_groups(parser)
  sort_action_groups(parser)

  return parser