Repository: in-toto/in-toto (language: python)

Issue:
Add support for DSSE
**Description of issue or feature request**:
The new signature wrapper [DSSE](https://github.com/secure-systems-lab/dsse) removes the in-toto's current dependence on canonicalization, and is also open to encodings other than JSON. Moving the signature wrapper definition out of the in-toto specification will also help it focus on its key goals, making it more readable. The switch to the new specification is documented in [ITE-5](https://github.com/in-toto/ITE/pull/13).

**Current behavior**: The reference implementation has no support for DSSE.


**Expected behavior**: The reference implementation must be capable of generating metadata using DSSE as an alternative to the current in-toto document format. In order to do this, the reference implementation must include a DSSE implementation.





Discussion / hints:
The current document format is shared with our sister project, TUF. The implementation for both projects is provided through [securesystemslib](https://github.com/secure-systems-lab/securesystemslib). The tooling for DSSE implemented can be prototyped here, in in-toto, before deciding how to integrate it with securesystemslib for TUF and other projects to take advantage of.
Hello! I would like to contribute to this project for GSOC'22. Can you please help me to get started with it?
Hi @a-muk, sure thing!

in-toto is a framework that generates metadata files which can be used to make certain verifiable claims about processes in the software supply chain. This metadata is cryptographically signed and represented using what we call a signature wrapper or envelope. The current signature wrapper is defined in the [in-toto specification](https://github.com/in-toto/docs/blob/master/in-toto-spec.md#42-file-formats-general-principles), with the model defined [here](https://github.com/in-toto/in-toto/blob/develop/in_toto/models/metadata.py#L39) in this implementation. Last year, we decided to retire this signature wrapper in favour of [DSSE](https://github.com/secure-systems-lab/dsse). So, the task detailed here is adding capabilities to this repository to generate in-toto metadata using DSSE as the wrapper rather than `Metablock` I've pointed to above.

Please let me know if you have any more questions. If you'd like to see current in-toto metadata represented using `Metablock`, I recommend playing with the [in-toto demo](https://github.com/in-toto/demo). You can compare their structure with the DSSE specification to see how they differ. Finally, feel free to join #in-toto on the [CNCF slack workspace](https://slack.cncf.io).
Thank you @adityasaky for the overview. 
Hello @adityasaky,

I am interested in this project for GSoC'22. I tried to understand the code and according to my understanding, currently `Metablock` handles the operation and need to implement a new model for metadata with DSSE with backward compatibility. Am I correct?

DSSE implementation example was also very clear.
I was reading the ITE-5 when I noticed it says "payload is expected to be a JSON file with a `_type` field" but according to DSSE protocol and envelope `payload` is `<Base64(SERIALIZED_BODY)>`.  I think there might be missing something or I have not understood it correctly.

Also, I love to have some suggestions to prepare for GSoC from you.
`SERIALIZED_BODY` is of type `bytes` and should be a JSON object which includes a `_type` field. The [protocol](https://github.com/secure-systems-lab/dsse/blob/master/protocol.md#signature-definition) only refers to `SERIALIZED_BODY` for the actual signature calculation while the envelope discusses using base64 encoding since DSSE supports signing arbitrary byte streams that may or may not be directly representable. Does that clarify? :)
Thanks, got it, byte stream cannot be represented in form of JSON thats why envelope encodes it in base64.

There is a [ecdsa.py](https://github.com/secure-systems-lab/dsse/blob/master/implementation/ecdsa.py) in the implementation of DSSE, which I believe provided by `securesystemslib` for in-toto metadata wrapper with DSSE or just have to write similar code when implementing?
Definitely use `securesystemslib`.

Fix patch:
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 3dcfa5ebc..4cc4b9d1e 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -60,7 +60,9 @@
 #
 html_theme = 'sphinx_rtd_theme'
 html_theme_options = {
-  'logo_only': True
+  'logo_only': True,
+  'collapse_navigation': False,
+  'navigation_depth': 3,
 }
 html_logo = 'in-toto-horizontal-white.png'
 html_favicon = 'in-toto-icon-color.png'
diff --git a/doc/source/model.rst b/doc/source/model.rst
index bf5e90edc..d2b03315a 100644
--- a/doc/source/model.rst
+++ b/doc/source/model.rst
@@ -10,6 +10,17 @@ and read or write in-toto conformant metadata (see
 :doc:`layout-creation-example`).
 
 
+Metadata
+-----------
+.. autoclass:: in_toto.models.metadata.Metadata
+  :inherited-members:
+  :members:
+
+DSSE Envelope
+-------------
+.. autoclass:: in_toto.models.metadata.Envelope
+  :inherited-members:
+  :members:
 
 Metablock
 ---------
diff --git a/in_toto/common_args.py b/in_toto/common_args.py
index fd6e0f1c3..c41b06fe6 100644
--- a/in_toto/common_args.py
+++ b/in_toto/common_args.py
@@ -162,6 +162,14 @@ def parse_password_and_prompt_args(args):
            " is not passed, the current working direcotry is used.")
 }
 
+DSSE_ARGS = ["--use-dsse"]
+DSSE_KWARGS = {
+  "dest": "use_dsse",
+  "default": False,
+  "action": "store_true",
+  "help": ("generate metadata using dsse (experimental)."),
+}
+
 
 def title_case_action_groups(parser):
   """Capitalize the first character of all words in the title of each action
diff --git a/in_toto/exceptions.py b/in_toto/exceptions.py
index 3579bcdb2..fb6798d23 100644
--- a/in_toto/exceptions.py
+++ b/in_toto/exceptions.py
@@ -29,3 +29,6 @@ class UnsupportedKeyTypeError(Error):
 
 class PrefixError(Error):
   """Indicates that there is an error because of the prefixes passed. """
+
+class InvalidMetadata(Error):
+  """Indicates that the metadata is not valid."""
diff --git a/in_toto/in_toto_mock.py b/in_toto/in_toto_mock.py
index 1fddb5672..8f781c592 100755
--- a/in_toto/in_toto_mock.py
+++ b/in_toto/in_toto_mock.py
@@ -29,7 +29,8 @@
 import argparse
 import logging
 import in_toto.runlib
-from in_toto.common_args import title_case_action_groups, sort_action_groups
+from in_toto.common_args import (title_case_action_groups, sort_action_groups,
+    DSSE_ARGS, DSSE_KWARGS)
 from in_toto import __version__
 
 # Command line interfaces should use in_toto base logger (c.f. in_toto.log)
@@ -80,6 +81,8 @@ def create_parser():
       " '<name>.link'. It is also used to associate the link with a step"
       " defined in an in-toto layout."))
 
+  parser.add_argument(*DSSE_ARGS, **DSSE_KWARGS)
+
   # FIXME: This is not yet ideal.
   # What should we do with tokens like > or ; ?
   parser.add_argument("link_cmd", nargs="+", metavar="<command>",
@@ -106,7 +109,7 @@ def main():
   LOG.setLevel(logging.INFO)
 
   try:
-    in_toto.runlib.in_toto_mock(args.name, args.link_cmd)
+    in_toto.runlib.in_toto_mock(args.name, args.link_cmd, args.use_dsse)
 
   except Exception as e:
     LOG.error("(in-toto-mock) {0}: {1}".format(type(e).__name__, e))
diff --git a/in_toto/in_toto_record.py b/in_toto/in_toto_record.py
index 57a4fe50a..7b21a3f72 100644
--- a/in_toto/in_toto_record.py
+++ b/in_toto/in_toto_record.py
@@ -39,7 +39,8 @@
     GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS, VERBOSE_KWARGS, QUIET_ARGS,
     QUIET_KWARGS, METADATA_DIRECTORY_ARGS, METADATA_DIRECTORY_KWARGS,
     KEY_PASSWORD_ARGS, KEY_PASSWORD_KWARGS, parse_password_and_prompt_args,
-    sort_action_groups, title_case_action_groups, OPTS_TITLE)
+    sort_action_groups, title_case_action_groups, OPTS_TITLE, DSSE_ARGS,
+    DSSE_KWARGS)
 
 from securesystemslib import interface
 
@@ -112,6 +113,7 @@ def create_parser():
   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)
+  parent_parser.add_argument(*DSSE_ARGS, **DSSE_KWARGS)
 
   verbosity_args = parent_parser.add_mutually_exclusive_group(required=False)
   verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
@@ -201,7 +203,7 @@ def main():
           signing_key=key, gpg_keyid=gpg_keyid,
           gpg_use_default=gpg_use_default, gpg_home=args.gpg_home,
           exclude_patterns=args.exclude_patterns, base_path=args.base_path,
-          lstrip_paths=args.lstrip_paths)
+          lstrip_paths=args.lstrip_paths, use_dsse=args.use_dsse)
 
     # Mutually exclusiveness is guaranteed by argparser
     else: # args.command == "stop":
diff --git a/in_toto/in_toto_run.py b/in_toto/in_toto_run.py
index 06b5b1d72..d3888fa5d 100755
--- a/in_toto/in_toto_run.py
+++ b/in_toto/in_toto_run.py
@@ -38,7 +38,8 @@
     GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS, VERBOSE_KWARGS, QUIET_ARGS,
     QUIET_KWARGS, METADATA_DIRECTORY_ARGS, METADATA_DIRECTORY_KWARGS,
     KEY_PASSWORD_ARGS, KEY_PASSWORD_KWARGS, parse_password_and_prompt_args,
-    sort_action_groups, title_case_action_groups, OPTS_TITLE)
+    sort_action_groups, title_case_action_groups, OPTS_TITLE, DSSE_ARGS,
+    DSSE_KWARGS,)
 
 from securesystemslib import interface
 
@@ -152,6 +153,7 @@ def create_parser():
   parser.add_argument(*BASE_PATH_ARGS, **BASE_PATH_KWARGS)
   parser.add_argument(*LSTRIP_PATHS_ARGS, **LSTRIP_PATHS_KWARGS)
   parser.add_argument(*METADATA_DIRECTORY_ARGS, **METADATA_DIRECTORY_KWARGS)
+  parser.add_argument(*DSSE_ARGS, **DSSE_KWARGS)
 
   verbosity_args = parser.add_mutually_exclusive_group(required=False)
   verbosity_args.add_argument(*VERBOSE_ARGS, **VERBOSE_KWARGS)
@@ -224,7 +226,7 @@ def main():
         gpg_keyid=gpg_keyid, gpg_use_default=gpg_use_default,
         gpg_home=args.gpg_home, exclude_patterns=args.exclude_patterns,
         base_path=args.base_path, lstrip_paths=args.lstrip_paths,
-        metadata_directory=args.metadata_directory)
+        metadata_directory=args.metadata_directory, use_dsse=args.use_dsse)
 
   except Exception as e:
     LOG.error("(in-toto-run) {0}: {1}".format(type(e).__name__, e))
diff --git a/in_toto/in_toto_sign.py b/in_toto/in_toto_sign.py
index ce21759f4..0c0c82985 100644
--- a/in_toto/in_toto_sign.py
+++ b/in_toto/in_toto_sign.py
@@ -28,7 +28,8 @@
 
 from in_toto import exceptions
 from in_toto.models.link import FILENAME_FORMAT
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Metadata
+from in_toto.models._signer import GPGSigner
 from in_toto.common_args import (GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS,
     VERBOSE_KWARGS, QUIET_ARGS, QUIET_KWARGS, title_case_action_groups,
     sort_action_groups)
@@ -37,8 +38,10 @@
     KEY_TYPE_ECDSA)
 
 import securesystemslib.formats
+import securesystemslib.exceptions
 from securesystemslib import interface
 from securesystemslib.gpg import functions as gpg_interface
+from securesystemslib.signer import SSlibSigner
 
 
 # Command line interfaces should use in_toto base logger (c.f. in_toto.log)
@@ -52,7 +55,7 @@ def _sign_and_dump_metadata(metadata, args):
 
   <Arguments>
     metadata:
-            Metablock object (contains Link or Layout object)
+            Metadata object (contains Link or Layout object)
     args:
             see argparser
 
@@ -75,12 +78,14 @@ def _sign_and_dump_metadata(metadata, args):
       # If `--gpg` was passed without argument we sign with the default key
       # Excluded so that coverage does not vary in different test environments
       if len(args.gpg) == 0: # pragma: no cover
-        signature = metadata.sign_gpg(gpg_keyid=None, gpg_home=args.gpg_home)
+        signature = metadata.create_signature(
+          GPGSigner(None, homedir=args.gpg_home))
 
       # Otherwise we sign with each passed keyid
       for keyid in args.gpg:
         securesystemslib.formats.KEYID_SCHEMA.check_match(keyid)
-        signature = metadata.sign_gpg(gpg_keyid=keyid, gpg_home=args.gpg_home)
+        signature = metadata.create_signature(
+            GPGSigner(keyid, homedir=args.gpg_home))
 
     # Alternatively we iterate over passed private key paths `--key KEYPATH
     # ...` load the corresponding key from disk and sign with it
@@ -97,7 +102,10 @@ def _sign_and_dump_metadata(metadata, args):
       for idx, key_path in enumerate(args.key):
         key = interface.import_privatekey_from_file(
             key_path, key_type=args.key_type[idx], prompt=args.prompt)
-        signature = metadata.sign(key)
+        signature = metadata.create_signature(SSlibSigner(key))
+
+    payload = metadata.get_payload()
+    _type = payload.type_
 
     # If `--output` was specified we store the signed link or layout metadata
     # to that location no matter what
@@ -106,17 +114,15 @@ def _sign_and_dump_metadata(metadata, args):
 
     # Otherwise, in case of links, we build the filename using the link/step
     # name and the keyid of the created signature (there is only one for links)
-    elif metadata.type_ == "link":
-      securesystemslib.formats.ANY_SIGNATURE_SCHEMA.check_match(signature)
-      keyid = signature["keyid"]
-      out_path = FILENAME_FORMAT.format(step_name=metadata.signed.name,
-          keyid=keyid)
+    elif _type == "link":
+      keyid = signature.keyid
+      out_path = FILENAME_FORMAT.format(step_name=payload.name, keyid=keyid)
 
     # In case of layouts we just override the input file.
-    elif metadata.type_ == "layout": # pragma: no branch
+    elif _type == "layout": # pragma: no branch
       out_path = args.file
 
-    LOG.info("Dumping {0} to '{1}'...".format(metadata.type_, out_path))
+    LOG.info("Dumping {0} to '{1}'...".format(_type, out_path))
 
     metadata.dump(out_path)
     sys.exit(0)
@@ -134,7 +140,7 @@ def _verify_metadata(metadata, args):
 
   <Arguments>
     metadata:
-            Metablock object (contains Link or Layout object)
+            Metadata object (contains Link or Layout object)
     args:
             see argparser
 
@@ -176,7 +182,7 @@ def _verify_metadata(metadata, args):
 def _load_metadata(file_path):
   """
   <Purpose>
-    Loads Metablock (link or layout metadata) file from disk
+    Loads Metadata (link or layout metadata) file from disk
 
   <Arguments>
     file_path:
@@ -186,11 +192,11 @@ def _load_metadata(file_path):
     SystemExit(2) if any exception occurs
 
   <Returns>
-    in-toto Metablock object (contains Link or Layout object)
+    in-toto Metadata object (contains Link or Layout object)
 
   """
   try:
-    return Metablock.load(file_path)
+    return Metadata.load(file_path)
 
   except Exception as e:
     LOG.error("The following error occurred while loading the file '{}': "
@@ -348,7 +354,8 @@ def main():
   metadata = _load_metadata(args.file)
 
   # Specific command line argument restrictions if we deal with links
-  if metadata.type_ == "link":
+  payload = metadata.get_payload()
+  if payload.type_ == "link":
     # Above we check that it's either `--key ...` or `--gpg ...`
     # Here we check that it is not more than one in each case when dealing
     # with links
diff --git a/in_toto/in_toto_verify.py b/in_toto/in_toto_verify.py
index e7500cd64..acc405bff 100755
--- a/in_toto/in_toto_verify.py
+++ b/in_toto/in_toto_verify.py
@@ -33,7 +33,7 @@
 from in_toto.common_args import (GPG_HOME_ARGS, GPG_HOME_KWARGS, VERBOSE_ARGS,
     VERBOSE_KWARGS, QUIET_ARGS, QUIET_KWARGS, title_case_action_groups,
     sort_action_groups, OPTS_TITLE)
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Metadata
 from in_toto import (
     __version__, SUPPORTED_KEY_TYPES, KEY_TYPE_RSA, KEY_TYPE_ED25519,
     KEY_TYPE_ECDSA)
@@ -176,7 +176,7 @@ def main():
 
   try:
     LOG.info("Loading layout...")
-    layout = Metablock.load(args.layout)
+    layout = Metadata.load(args.layout)
 
     layout_key_dict = {}
     if args.layout_keys is not None:
diff --git a/in_toto/models/metadata.py b/in_toto/models/metadata.py
index b596bfb96..b4fde3255 100644
--- a/in_toto/models/metadata.py
+++ b/in_toto/models/metadata.py
@@ -24,19 +24,177 @@
 
 import attr
 import json
+from typing import Union
 
 import securesystemslib.keys
 import securesystemslib.formats
 import securesystemslib.exceptions
 import securesystemslib.gpg.functions
+from securesystemslib.exceptions import VerificationError
+from securesystemslib.signer import Signature, Signer, SSlibKey
+from securesystemslib.dsse import Envelope as SSlibEnvelope
 
-from in_toto.models.common import ValidationMixin
+from in_toto.exceptions import InvalidMetadata, SignatureVerificationError
+from in_toto.models._signer import GPGSigner
+from in_toto.models.common import Signable, ValidationMixin
 from in_toto.models.link import Link
 from in_toto.models.layout import Layout
-from in_toto.exceptions import SignatureVerificationError
+
+
+ENVELOPE_PAYLOAD_TYPE = "application/vnd.in-toto+json"
+
+
+class Metadata:
+  """A Metadata abstraction between DSSE Envelope and Metablock."""
+
+  @classmethod
+  def from_dict(cls, data):
+    """Loads DSSE or Traditional Metadata from its JSON/dict representation."""
+
+    if "payload" in data:
+      if data.get("payloadType") == ENVELOPE_PAYLOAD_TYPE:
+        return Envelope.from_dict(data)
+
+    elif "signed" in data:
+      return Metablock.from_dict(data)
+
+    raise InvalidMetadata
+
+  def to_dict(self):
+    """Returns the JSON-serializable dictionary representation of self."""
+    raise NotImplementedError  # pragma: no cover
+
+  @classmethod
+  def load(cls, path):
+    """Loads the JSON string representation of metadata from disk.
+
+    Arguments:
+      path: The path to read the file from.
+
+    Raises:
+      IOError: The file cannot be read.
+      InvalidMetadata: Metadata format is invalid.
+      securesystemslib.exceptions.FormatError: Metadata format is invalid.
+
+    Returns:
+      A Metadata containing a Link or Layout object.
+
+    """
+    with open(path, "r", encoding="utf8") as fp:
+      data = json.load(fp)
+
+    return cls.from_dict(data)
+
+  def dump(self, path):
+    """Writes the JSON string representation of the instance to disk.
+
+    Arguments:
+      path: The path to write the file to.
+
+    Raises:
+      IOError: File cannot be written.
+
+    """
+    json_bytes = json.dumps(
+      self.to_dict(),
+      sort_keys=True,
+    ).encode("utf-8")
+
+    with open(path, "wb") as fp:
+      fp.write(json_bytes)
+
+  def create_signature(self, signer: Signer) -> Signature:
+    """Creates signature over signable with Signer and adds it to signatures.
+
+    Arguments:
+      signer: A "Signer" class instance to create signature.
+
+    Raises:
+      securesystemslib.exceptions.CryptoError, \
+              securesystemslib.exceptions.UnsupportedAlgorithmError:
+          Signing errors.
+
+    Returns:
+      The signature. A securesystemslib.signer.Signature type.
+    """
+    raise NotImplementedError  # pragma: no cover
+
+  def verify_signature(self, verification_key):
+    """Verifies a signature over signable in signatures with verification_key.
+
+    Arguments:
+      verification_key: A verification key. The format is
+          securesystemslib.formats.ANY_VERIFICATION_KEY_SCHEMA.
+
+    Raises:
+      securesystemslib.exceptions.FormatError: The passed key is malformed.
+
+      SignatureVerificationError: No signature keyid matches
+          the verification key keyid, or the matching signature is malformed,
+          or the matching signature is invalid.
+    """
+    raise NotImplementedError  # pragma: no cover
+
+  def get_payload(self):
+    """Returns ``Link`` or ``Layout``."""
+    raise NotImplementedError  # pragma: no cover
+
+
+class Envelope(SSlibEnvelope, Metadata):
+  """DSSE Envelope for in-toto payloads."""
+
+  @classmethod
+  def from_signable(cls, signable: Signable) -> "Envelope":
+    """Creates DSSE envelope with signable bytes as payload."""
+
+    json_bytes = json.dumps(
+      attr.asdict(signable),
+      sort_keys=True,
+    ).encode("utf-8")
+
+    return cls(
+      payload=json_bytes,
+      payload_type=ENVELOPE_PAYLOAD_TYPE,
+      signatures=[]
+    )
+
+  def create_signature(self, signer: Signer) -> Signature:
+    if isinstance(signer, GPGSigner):
+      raise NotImplementedError("GPG Signing is not implemented")
+
+    return super().sign(signer)
+
+  def verify_signature(self, verification_key):
+    key = SSlibKey.from_securesystemslib_key(verification_key)
+    try:
+      super().verify(
+        keys=[key], threshold=1
+      )
+    except VerificationError as exc:
+      raise SignatureVerificationError from exc
+
+  def get_payload(self) -> Union[Link, Layout]:
+    """Parse DSSE payload into Link or Layout object.
+
+      Raises:
+          InvalidMetadata: If type in payload is not ``link`` or ``layout``.
+
+      Returns:
+          Link or Layout.
+    """
+
+    data = json.loads(self.payload.decode("utf-8"))
+    _type = data.get("_type")
+    if _type == "link":
+      return Link.read(data)
+    if _type == "layout":
+      return Layout.read(data)
+
+    raise InvalidMetadata
+
 
 @attr.s(repr=False, init=False)
-class Metablock(ValidationMixin):
+class Metablock(Metadata, ValidationMixin):
   """A container for signed in-toto metadata.
 
   Provides methods for metadata JSON (de-)serialization, reading from and
@@ -93,24 +251,9 @@ def dump(self, path):
       fp.write("{}".format(self).encode("utf-8"))
 
 
-  @staticmethod
-  def load(path):
-    """Loads the JSON string representation of in-toto metadata from disk.
-
-    Arguments:
-      path: The path to read the file from.
-
-    Raises:
-      IOError: The file cannot be read.
-      securesystemslib.exceptions.FormatError: Metadata format is invalid.
-
-    Returns:
-      A Metablock object whose signable attribute is either a Link or a Layout
-      object.
-
-    """
-    with open(path, "r", encoding="utf8") as fp:
-      data = json.load(fp)
+  @classmethod
+  def from_dict(cls, data):
+    """Creates a Metablock object from its JSON/dict representation."""
 
     signatures = data.get("signatures", [])
     signed_data = data.get("signed", {})
@@ -125,7 +268,16 @@ def load(path):
     else:
       raise securesystemslib.exceptions.FormatError("Invalid Metadata format")
 
-    return Metablock(signatures=signatures, signed=signed)
+    return cls(signatures=signatures, signed=signed)
+
+
+  def to_dict(self):
+    """Returns the JSON-serializable dictionary representation of self."""
+
+    return {
+      "signatures": self.signatures,
+      "signed": attr.asdict(self.signed)
+    }
 
 
   @property
@@ -137,6 +289,12 @@ def type_(self):
     return self.signed.type_
 
 
+  def create_signature(self, signer: Signer):
+    signature = signer.sign(self.signed.signable_bytes)
+    self.signatures.append(signature.to_dict())
+
+    return signature
+
   def sign(self, key):
     """Creates signature over signable with key and adds it to signatures.
 
@@ -282,3 +440,8 @@ def _validate_signatures(self):
 
     for signature in self.signatures:
       securesystemslib.formats.ANY_SIGNATURE_SCHEMA.check_match(signature)
+
+  def get_payload(self):
+    """Returns signed of the Metablock."""
+
+    return self.signed
diff --git a/in_toto/runlib.py b/in_toto/runlib.py
index 30d441184..4560529ea 100644
--- a/in_toto/runlib.py
+++ b/in_toto/runlib.py
@@ -24,7 +24,7 @@
     - Execute command
       - Capture stdout/stderr/return value of the executed command
     - Record state of product (files after the command was executed)
-    - Return Metablock containing a Link object which can be can be signed
+    - Return Metadata containing a Link object which can be can be signed
       and stored to disk
 """
 import glob
@@ -42,15 +42,17 @@
 import in_toto.settings
 import in_toto.exceptions
 
+from in_toto.models._signer import GPGSigner
 from in_toto.models.link import (UNFINISHED_FILENAME_FORMAT, FILENAME_FORMAT,
     FILENAME_FORMAT_SHORT, UNFINISHED_FILENAME_FORMAT_GLOB)
+from in_toto.models.metadata import (Metadata, Envelope, Metablock)
 
 import securesystemslib.formats
 import securesystemslib.hash
 import securesystemslib.exceptions
 import securesystemslib.gpg
+from securesystemslib.signer import SSlibSigner, Signature
 
-from in_toto.models.metadata import Metablock
 
 
 # Inherits from in_toto base logger (c.f. in_toto.log)
@@ -462,7 +464,7 @@ def execute_link(link_cmd_args, record_streams):
     }
 
 
-def in_toto_mock(name, link_cmd_args):
+def in_toto_mock(name, link_cmd_args, use_dsse=False):
   """
   <Purpose>
     in_toto_run with defaults
@@ -477,6 +479,8 @@ def in_toto_mock(name, link_cmd_args):
     link_cmd_args:
             A list where the first element is a command and the remaining
             elements are arguments passed to that command.
+    use_dsse (optional):
+            A boolean indicating if DSSE should be used to generate metadata.
 
   <Exceptions>
     None.
@@ -486,11 +490,11 @@ def in_toto_mock(name, link_cmd_args):
     from link.FILENAME_FORMAT_SHORT
 
   <Returns>
-    Newly created Metablock object containing a Link object
+    Newly created Metadata object containing a Link object
 
   """
   link_metadata = in_toto_run(name, ["."], ["."], link_cmd_args,
-      record_streams=True)
+      record_streams=True, use_dsse=use_dsse)
 
   filename = FILENAME_FORMAT_SHORT.format(step_name=name)
   LOG.info("Storing unsigned link metadata to '{}'...".format(filename))
@@ -513,7 +517,8 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
     record_streams=False, signing_key=None, gpg_keyid=None,
     gpg_use_default=False, gpg_home=None, exclude_patterns=None,
     base_path=None, compact_json=False, record_environment=False,
-    normalize_line_endings=False, lstrip_paths=None, metadata_directory=None):
+    normalize_line_endings=False, lstrip_paths=None, metadata_directory=None,
+    use_dsse=False):
   """Performs a supply chain step or inspection generating link metadata.
 
   Executes link_cmd_args, recording paths and hashes of files before and after
@@ -574,6 +579,9 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
     metadata_directory (optional): A directory path to write the resulting link
         metadata file to. Default destination is the current working directory.
 
+    use_dsse (optional): A boolean indicating if DSSE should be used to
+        generate metadata.
+
   Raises:
     securesystemslib.exceptions.FormatError: Passed arguments are malformed.
 
@@ -603,7 +611,7 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
     Writes link metadata file to disk, if any key argument is passed.
 
   Returns:
-    A Metablock object that contains the resulting link object.
+    A Metadata object that contains the resulting link object.
 
   """
   LOG.info("Running '{}'...".format(name))
@@ -657,24 +665,31 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
       materials=materials_dict, products=products_dict, command=link_cmd_args,
       byproducts=byproducts, environment=environment)
 
-  link_metadata = Metablock(signed=link, compact_json=compact_json)
+  if use_dsse:
+    LOG.info("Generating link metadata using DSSE...")
+    link_metadata = Envelope.from_signable(link)
+  else:
+    LOG.info("Generating link metadata using Metablock...")
+    link_metadata = Metablock(signed=link, compact_json=compact_json)
 
-  signature = None
+  signer = None
   if signing_key:
     LOG.info("Signing link metadata using passed key...")
-    signature = link_metadata.sign(signing_key)
+    signer = SSlibSigner(signing_key)
 
   elif gpg_keyid:
     LOG.info("Signing link metadata using passed GPG keyid...")
-    signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home)
+    signer = GPGSigner(keyid=gpg_keyid, homedir=gpg_home)
 
   elif gpg_use_default:
     LOG.info("Signing link metadata using default GPG key ...")
-    signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home)
+    signer = GPGSigner(keyid=None, homedir=gpg_home)
 
   # We need the signature's keyid to write the link to keyid infix'ed filename
-  if signature:
-    signing_keyid = signature["keyid"]
+  if signer:
+    signature = link_metadata.create_signature(signer)
+    signing_keyid = signature.keyid
+
     filename = FILENAME_FORMAT.format(step_name=name, keyid=signing_keyid)
 
     if metadata_directory is not None:
@@ -689,7 +704,7 @@ def in_toto_run(name, material_list, product_list, link_cmd_args,
 def in_toto_record_start(step_name, material_list, signing_key=None,
     gpg_keyid=None, gpg_use_default=False, gpg_home=None,
     exclude_patterns=None, base_path=None, record_environment=False,
-    normalize_line_endings=False, lstrip_paths=None):
+    normalize_line_endings=False, lstrip_paths=None, use_dsse=False):
   """Generates preliminary link metadata.
 
   Records paths and hashes of materials in a preliminary link metadata file.
@@ -737,6 +752,9 @@ def in_toto_record_start(step_name, material_list, signing_key=None,
     lstrip_paths (optional): A list of path prefixes used to left-strip
         artifact paths before storing them in the resulting link metadata.
 
+    use_dsse (optional): A boolean indicating if DSSE should be used to
+        generate metadata.
+
   Raises:
     securesystemslib.exceptions.FormatError: Passed arguments are malformed.
 
@@ -802,22 +820,28 @@ def in_toto_record_start(step_name, material_list, signing_key=None,
           materials=materials_dict, products={}, command=[], byproducts={},
           environment=environment)
 
-  link_metadata = Metablock(signed=link)
+  if use_dsse:
+    LOG.info("Generating link metadata using DSSE...")
+    link_metadata = Envelope.from_signable(link)
+  else:
+    LOG.info("Generating link metadata using Metablock...")
+    link_metadata = Metablock(signed=link)
 
   if signing_key:
     LOG.info("Signing link metadata using passed key...")
-    signature = link_metadata.sign(signing_key)
+    signer = SSlibSigner(signing_key)
 
   elif gpg_keyid:
     LOG.info("Signing link metadata using passed GPG keyid...")
-    signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home)
+    signer = GPGSigner(keyid=gpg_keyid, homedir=gpg_home)
 
   else:  # (gpg_use_default)
     LOG.info("Signing link metadata using default GPG key ...")
-    signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home)
+    signer = GPGSigner(keyid=None, homedir=gpg_home)
 
+  signature = link_metadata.create_signature(signer)
   # We need the signature's keyid to write the link to keyid infix'ed filename
-  signing_keyid = signature["keyid"]
+  signing_keyid = signature.keyid
 
   unfinished_fn = UNFINISHED_FILENAME_FORMAT.format(step_name=step_name,
     keyid=signing_keyid)
@@ -961,7 +985,7 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
     unfinished_fn = unfinished_fn_list[0]
 
   LOG.info("Loading preliminary link metadata '{}'...".format(unfinished_fn))
-  link_metadata = Metablock.load(unfinished_fn)
+  link_metadata = Metadata.load(unfinished_fn)
 
   # The file must have been signed by the same key
   # If we have a signing_key we use it for verification as well
@@ -986,33 +1010,48 @@ def in_toto_record_stop(step_name, product_list, signing_key=None,
     # accepts many different ids (mail, name, parts of an id, ...) but we
     # need a specific format.
     LOG.info("Verifying preliminary link signature using default gpg key...")
-    keyid = link_metadata.signatures[0]["keyid"]
+    # signatures are objects in DSSE.
+    sig = link_metadata.signatures[0]
+    if isinstance(sig, Signature):
+      keyid = sig.keyid
+    else:
+      keyid = sig["keyid"]
     gpg_pubkey = securesystemslib.gpg.functions.export_pubkey(
         keyid, gpg_home)
     verification_key = gpg_pubkey
 
   link_metadata.verify_signature(verification_key)
 
+  LOG.info("Extracting Link from metadata...")
+  link = link_metadata.get_payload()
+
   # Record products if a product path list was passed
   if product_list:
     LOG.info("Recording products '{}'...".format(", ".join(product_list)))
 
-  link_metadata.signed.products = record_artifacts_as_dict(
+  link.products = record_artifacts_as_dict(
       product_list, exclude_patterns=exclude_patterns, base_path=base_path,
       follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings,
       lstrip_paths=lstrip_paths)
 
-  link_metadata.signatures = []
+  if isinstance(link_metadata, Metablock):
+    LOG.info("Generating link metadata using Metablock...")
+    link_metadata = Metablock(signed=link)
+  else:
+    LOG.info("Generating link metadata using DSSE...")
+    link_metadata = Envelope.from_signable(link)
+
   if signing_key:
     LOG.info("Updating signature with key '{:.8}...'...".format(keyid))
-    link_metadata.sign(signing_key)
+    signer = SSlibSigner(signing_key)
 
   else: # gpg_keyid or gpg_use_default
     # In both cases we use the keyid we got from verifying the preliminary
     # link signature above.
     LOG.info("Updating signature with gpg key '{:.8}...'...".format(keyid))
-    link_metadata.sign_gpg(keyid, gpg_home)
+    signer = GPGSigner(keyid=keyid, homedir=gpg_home)
 
+  link_metadata.create_signature(signer)
   fn = FILENAME_FORMAT.format(step_name=step_name, keyid=keyid)
 
   if metadata_directory is not None:
diff --git a/in_toto/verifylib.py b/in_toto/verifylib.py
index dac07a1d4..8cb82c35b 100644
--- a/in_toto/verifylib.py
+++ b/in_toto/verifylib.py
@@ -39,13 +39,15 @@
 import in_toto.models.layout
 import in_toto.models.link
 import in_toto.formats
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Metadata
 from in_toto.exceptions import (RuleVerificationError, LayoutExpiredError,
     ThresholdVerificationError, BadReturnValueError,
     SignatureVerificationError)
-from securesystemslib.gpg.exceptions import KeyExpirationError
 import in_toto.rulelib
 
+import securesystemslib.formats
+from securesystemslib.gpg.exceptions import KeyExpirationError
+
 # Inherits from in_toto base logger (c.f. in_toto.log)
 LOG = logging.getLogger(__name__)
 
@@ -122,7 +124,7 @@ def load_links_for_layout(layout, link_dir_path):
 
     {
       <step name> : {
-        <functionary key id> : <Metablock containing a Link or Layout object>,
+        <functionary key id> : <Metadata containing a Link or Layout object>,
         ...
       }, ...
     }
@@ -148,7 +150,7 @@ def load_links_for_layout(layout, link_dir_path):
         filepath = os.path.join(link_dir_path, filename)
 
         try:
-          metadata = Metablock.load(filepath)
+          metadata = Metadata.load(filepath)
           links_per_step[keyid] = metadata
 
         except IOError:
@@ -172,8 +174,7 @@ def run_all_inspections(layout, persist_inspection_links):
   <Purpose>
     Extracts all inspections from a passed Layout's inspect field and
     iteratively runs each command defined in the Inspection's `run` field using
-    `runlib.in_toto_run`, which returns a Metablock object containing a Link
-    object.
+    `runlib.in_toto_run`, which returns a Link object.
 
     If a link command returns non-zero the verification is aborted.
 
@@ -194,7 +195,7 @@ def run_all_inspections(layout, persist_inspection_links):
 
     {
       <inspection name> : {
-        <Metablock containing a Link object>,
+        <Link object>,
         ...
       }, ...
     }
@@ -222,7 +223,7 @@ def run_all_inspections(layout, persist_inspection_links):
         link.signed.byproducts.get("return-value"),
         inspection.run)
 
-    inspection_links_dict[inspection.name] = link
+    inspection_links_dict[inspection.name] = link.signed
 
     # If client requests persistent inspection links,
     # Dump the inspection link file for auditing
@@ -338,18 +339,18 @@ def substitute_parameters(layout, parameter_dictionary):
 
 
 
-def verify_layout_signatures(layout_metablock, keys_dict):
+def verify_metadata_signatures(metadata, keys_dict):
   """
   <Purpose>
-    Iteratively verifies the signatures of a Metablock object containing
+    Iteratively verifies the signatures of a Metadata object containing
     a Layout object for every verification key in the passed keys dictionary.
 
     Requires at least one key to be passed and requires every passed key to
     find a valid signature.
 
   <Arguments>
-    layout_metablock:
-            A Metablock object containing a Layout whose signatures are
+    metadata:
+            A Metadata object containing a Layout whose signatures are
             verified.
 
     keys_dict:
@@ -377,16 +378,16 @@ def verify_layout_signatures(layout_metablock, keys_dict):
 
   # Fail if any of the passed keys can't verify a signature on the Layout
   for junk, verify_key in keys_dict.items():
-    layout_metablock.verify_signature(verify_key)
+    metadata.verify_signature(verify_key)
 
 
-def verify_link_signature_thresholds(layout, chain_link_dict):
+def verify_link_signature_thresholds(layout, steps_metadata):
   """
   <Purpose>
     Verify that for each step of the layout there are at least `threshold`
-    links, signed by different authorized functionaries and return the chain
-    link dictionary containing only authorized links whose signatures
-    were successfully verified.
+    links metadata, signed by different authorized functionaries and return the
+    verified link metadata dictionary containing only authorized links metadata
+    whose signatures were successfully verified.
 
     NOTE: If the layout's key store (`layout.keys`) lists a (master) key `K`,
     with a subkey `K'`, then `K'` is authorized implicitly, to sign any link
@@ -397,12 +398,12 @@ def verify_link_signature_thresholds(layout, chain_link_dict):
     layout:
             A Layout object whose Steps are extracted and verified.
 
-    chain_link_dict:
+    steps_metadata:
             A dictionary containing link metadata per functionary per step,
             e.g.:
             {
               <link name> : {
-                <functionary key id> : <Metablock containing a Link or Layout
+                <functionary key id> : <Metadata containing a Link or Layout
                                         object>,
                 ...
               }, ...
@@ -415,7 +416,7 @@ def verify_link_signature_thresholds(layout, chain_link_dict):
             functionaries.
 
   <Returns>
-    A chain_link_dict containing only links with valid signatures created by
+    A steps_metadata containing only links with valid signatures created by
     authorized functionaries.
 
   """
@@ -430,7 +431,7 @@ def verify_link_signature_thresholds(layout, chain_link_dict):
       main_keys_for_subkeys[sub_keyid] = main_key
 
   # Dict for valid and authorized links of all steps of the layout
-  verfied_chain_link_dict = {}
+  verified_steps_metadata = {}
 
   # For each step of the layout check the signatures of corresponding links.
   # Consider only links where the signature is valid and keys are authorized,
@@ -446,7 +447,7 @@ def verify_link_signature_thresholds(layout, chain_link_dict):
     used_main_keyids = []
 
     # Do per step link threshold verification
-    for link_keyid, link in chain_link_dict.get(step.name, {}).items():
+    for link_keyid, link in steps_metadata.get(step.name, {}).items():
       # Iterate over authorized keyids to find a key or subkey corresponding
       # to the given link and check if the link's keyid is authorized.
       # Subkeys of authorized main keys are authorized implicitly.
@@ -516,11 +517,11 @@ def verify_link_signature_thresholds(layout, chain_link_dict):
           valid_authorized_links_cnt))
 
     # Add all good links of this step to the dictionary of links of all steps
-    verfied_chain_link_dict[step.name] = verified_key_link_dict
+    verified_steps_metadata[step.name] = verified_key_link_dict
 
   # Threshold verification succeeded, return valid and authorized links for
   # further verification
-  return verfied_chain_link_dict
+  return verified_steps_metadata
 
 def verify_command_alignment(command, expected_command):
   """
@@ -572,7 +573,7 @@ def verify_all_steps_command_alignment(layout, chain_link_dict):
             e.g.:
             {
               <link name> : {
-                <functionary key id> : <Metablock containing a Link object>,
+                <functionary key id> : <Link object>,
                 ...
               }, ...
             }
@@ -596,7 +597,7 @@ def verify_all_steps_command_alignment(layout, chain_link_dict):
           in_toto.models.link.FILENAME_FORMAT.format(step_name=step.name,
               keyid=keyid)))
 
-      command = link.signed.command
+      command = link.command
       verify_command_alignment(command, expected_command)
 
 
@@ -631,7 +632,7 @@ def verify_match_rule(rule_data, artifacts_queue, source_artifacts, links):
     links:
             A dictionary containing link metadata per step or inspection, e.g.:
             {
-              <link name> : <Metablock containing a link object>,
+              <link name> : <Link object>,
               ...
             }
 
@@ -653,7 +654,7 @@ def verify_match_rule(rule_data, artifacts_queue, source_artifacts, links):
     return consumed
 
   # Extract destination artifacts from destination link
-  dest_artifacts = getattr(dest_link.signed, rule_data["dest_type"])
+  dest_artifacts = getattr(dest_link, rule_data["dest_type"])
 
   # Filter part 1 - Filter artifacts using optional source prefix, and subtract
   # prefix before filtering with rule pattern (see filter part 2) to prevent
@@ -1002,7 +1003,7 @@ def verify_item_rules(source_name, source_type, rules, links):
     links:
             A dictionary containing link metadata per step or inspection, e.g.:
             {
-              <link name> : <Metablock containing a link>,
+              <link name> : <Link object>,
               ...
             }
 
@@ -1027,8 +1028,8 @@ def verify_item_rules(source_name, source_type, rules, links):
 
   # Create shortcuts to item's materials and products (including hashes),
   # required to verify "modify" and "match" rules.
-  materials_dict = links[source_name].signed.materials
-  products_dict = links[source_name].signed.products
+  materials_dict = links[source_name].materials
+  products_dict = links[source_name].products
 
   # All other rules only require materials or products paths (without hashes)
   materials_paths = set(materials_dict.keys())
@@ -1037,7 +1038,7 @@ def verify_item_rules(source_name, source_type, rules, links):
   # Depending on the source type we create the artifact queue from the item's
   # materials or products and use it to keep track of (not) consumed artifacts.
   # The queue also only contains aritfact keys (without hashes)
-  artifacts = getattr(links[source_name].signed, source_type)
+  artifacts = getattr(links[source_name], source_type)
   artifacts_queue = set(artifacts.keys())
 
   # Reset and re-populate rule traceback info dict for a rich error message
@@ -1115,7 +1116,7 @@ def verify_all_item_rules(items, links):
     links:
             A dictionary containing link metadata per step or inspection, e.g.:
             {
-              <link name> : <Metablock containing a Link object>,
+              <link name> : <Link object>,
               ...
             }
 
@@ -1154,7 +1155,7 @@ def verify_threshold_constraints(layout, chain_link_dict):
             e.g.:
             {
               <link name> : {
-                <functionary key id> : <Metablock containing a Link object>,
+                <functionary key id> : <Link object>,
                 ...
               }, ...
             }
@@ -1200,8 +1201,8 @@ def verify_threshold_constraints(layout, chain_link_dict):
     for keyid, link in key_link_dict.items():
       # TODO: Do we only care for artifacts, or do we want to
       # assert equality of other properties as well?
-      if (reference_link.signed.materials != link.signed.materials or
-          reference_link.signed.products != link.signed.products):
+      if (reference_link.materials != link.materials or
+          reference_link.products != link.products):
         raise ThresholdVerificationError("Links '{0}' and '{1}' have different"
             " artifacts!".format(
                 in_toto.models.link.FILENAME_FORMAT.format(
@@ -1228,7 +1229,7 @@ def reduce_chain_links(chain_link_dict):
             e.g.:
             {
               <link name> : {
-                <functionary key id> : <Metablock containing a Link object>,
+                <functionary key id> : <Link object>,
                 ...
               }, ...
             }
@@ -1256,23 +1257,24 @@ def reduce_chain_links(chain_link_dict):
   return reduced_chain_link_dict
 
 
-def verify_sublayouts(layout, chain_link_dict, superlayout_link_dir_path):
+def verify_sublayouts(layout, steps_metadata, superlayout_link_dir_path):
   """
   <Purpose>
-    Checks if any step has been delegated by the functionary, recurses into
-    the delegation and replaces the layout object in the chain_link_dict
-    by an equivalent link object.
+    Extracts link or layout object for each step in steps_metadata. Checks if
+    any step has been delegated by the functionary, recurses into the
+    delegation, and replaces the layout object with an equivalent link object.
+    Returns the extracted link objects in a dict called chain_link_dict.
 
   <Arguments>
     layout:
             The layout specified by the project owner.
 
-    chain_link_dict:
+    steps_metadata:
             A dictionary containing link metadata per functionary per step,
             e.g.:
             {
               <link name> : {
-                <functionary key id> : <Metablock containing a Link or Layout
+                <functionary key id> : <Metadata containing a Link or Layout
                                           object>,
                 ...
               }, ...
@@ -1291,22 +1293,28 @@ def verify_sublayouts(layout, chain_link_dict, superlayout_link_dir_path):
     None.
 
   <Returns>
-    The passed dictionary containing link metadata per functionary per step,
-    with layouts replaced with summary links.
+    The passed dictionary containing Link objects instead of metadata per
+    functionary per step, with layouts replaced with summary links.
     e.g.:
     {
       <link name> : {
-        <functionary key id> : <Metablock containing a Link object>,
+        <functionary key id> : <Link object>,
         ...
       }, ...
     }
 
   """
-  for step_name, key_link_dict in chain_link_dict.items():
+  chain_link_dict = {}
 
-    for keyid, link in key_link_dict.items():
+  for step_name, metadata_dict in steps_metadata.items():
+
+    key_link_dict = {}
+    for keyid, metadata in metadata_dict.items():
+
+      payload = metadata.get_payload()
+
+      if payload.type_ == "layout":
 
-      if link.type_ == "layout":
         LOG.info("Verifying sublayout {}...".format(step_name))
         layout_key_dict = {}
 
@@ -1326,12 +1334,15 @@ def verify_sublayouts(layout, chain_link_dict, superlayout_link_dir_path):
 
         # Make a recursive call to in_toto_verify with the
         # layout and the extracted key object
-        summary_link = in_toto_verify(link, layout_key_dict,
+        summary_link = in_toto_verify(metadata, layout_key_dict,
             link_dir_path=sublayout_link_dir_path, step_name=step_name)
 
-        # Replace the layout object in the passed chain_link_dict
-        # with the link file returned by in-toto-verify
-        key_link_dict[keyid] = summary_link
+        # Replace the layout object with the link object returned
+        # by in-toto-verify
+        payload = summary_link
+
+      key_link_dict[keyid] = payload
+    chain_link_dict[step_name] = key_link_dict
 
   return chain_link_dict
 
@@ -1356,7 +1367,7 @@ def get_summary_link(layout, reduced_chain_link_dict, name):
             A dictionary containing link metadata per step,
             e.g.:
             {
-              <link name> : <Metablock containing a Link object>,
+              <link name> : <Link object>,
               ...
             }
     name:
@@ -1369,8 +1380,8 @@ def get_summary_link(layout, reduced_chain_link_dict, name):
     None.
 
   <Returns>
-    A Metablock object containing a Link which summarizes the materials and
-    products of the overall software supply chain.
+    A Link object which summarizes the materials and products of the overall
+    software supply chain.
 
   """
   # Create empty link object
@@ -1382,17 +1393,17 @@ def get_summary_link(layout, reduced_chain_link_dict, name):
     first_step_link = reduced_chain_link_dict[layout.steps[0].name]
     last_step_link = reduced_chain_link_dict[layout.steps[-1].name]
 
-    summary_link.materials = first_step_link.signed.materials
+    summary_link.materials = first_step_link.materials
     summary_link.name = name
 
-    summary_link.products = last_step_link.signed.products
-    summary_link.byproducts = last_step_link.signed.byproducts
-    summary_link.command = last_step_link.signed.command
+    summary_link.products = last_step_link.products
+    summary_link.byproducts = last_step_link.byproducts
+    summary_link.command = last_step_link.command
 
-  return Metablock(signed=summary_link)
+  return summary_link
 
 
-def in_toto_verify(layout, layout_key_dict, link_dir_path=".",
+def in_toto_verify(metadata, layout_key_dict, link_dir_path=".",
     substitution_parameters=None, step_name="",
     persist_inspection_links=True):
   """Performs complete in-toto supply chain verification for a final product.
@@ -1416,7 +1427,7 @@ def in_toto_verify(layout, layout_key_dict, link_dir_path=".",
   11. Process inspection product and material rules
 
   Arguments:
-    layout: A Metablock object that contains a Layout object to be verified.
+    metadata: A Metadata object that contains a Layout object to be verified.
 
     layout_key_dict: A public key dictionary. The verification routine requires
         at least one key, and a valid signature on the layout for each key.
@@ -1465,18 +1476,20 @@ def in_toto_verify(layout, layout_key_dict, link_dir_path=".",
     Runs inspection commands in subprocess.
 
   Returns:
-    A Metablock object that contains a Link object, which summarizes the
-    materials and products of the overall software supply chain. This is mostly
-    useful during recursive sublayout verification.
+    A Link object, which summarizes the materials and products of the overall
+    software supply chain. This is mostly useful during recursive sublayout
+    verification.
 
   """
-  LOG.info("Verifying layout signatures...")
-  verify_layout_signatures(layout, layout_key_dict)
+
+  LOG.info("Verifying layout metadata signatures...")
+  verify_metadata_signatures(metadata, layout_key_dict)
 
   # For the rest of the verification we only care about the layout payload
   # (Layout) that carries all the information and not about the layout
   # container (Metablock) that also carries the signatures
-  layout = layout.signed
+  LOG.info("Extracting layout from metadata...")
+  layout = metadata.get_payload()
 
   LOG.info("Verifying layout expiration...")
   verify_layout_expiration(layout)
@@ -1487,13 +1500,13 @@ def in_toto_verify(layout, layout_key_dict, link_dir_path=".",
     substitute_parameters(layout, substitution_parameters)
 
   LOG.info("Reading link metadata files...")
-  chain_link_dict = load_links_for_layout(layout, link_dir_path)
+  steps_metadata = load_links_for_layout(layout, link_dir_path)
 
   LOG.info("Verifying link metadata signatures...")
-  chain_link_dict = verify_link_signature_thresholds(layout, chain_link_dict)
+  steps_metadata = verify_link_signature_thresholds(layout, steps_metadata)
 
   LOG.info("Verifying sublayouts...")
-  chain_link_dict = verify_sublayouts(layout, chain_link_dict, link_dir_path)
+  chain_link_dict = verify_sublayouts(layout, steps_metadata, link_dir_path)
 
   LOG.info("Verifying alignment of reported commands...")
   verify_all_steps_command_alignment(layout, chain_link_dict)
diff --git a/requirements-pinned.txt b/requirements-pinned.txt
index 45021da75..38d2cbabc 100644
--- a/requirements-pinned.txt
+++ b/requirements-pinned.txt
@@ -22,7 +22,7 @@ pynacl==1.5.0
     # via securesystemslib
 python-dateutil==2.8.2
     # via -r requirements.txt
-securesystemslib[crypto,pynacl]==0.26.0
+securesystemslib[crypto,pynacl]==0.27.0
     # via -r requirements.txt
 six==1.16.0
     # via python-dateutil
diff --git a/requirements.txt b/requirements.txt
index 623f6d9fa..b66059be3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@
 #
 # 'requirements-pinned.txt' is updated on GitHub with Dependabot, which
 # triggers CI/CD builds to automatically test against updated dependencies.
-securesystemslib[crypto,pynacl]>=0.26.0
+securesystemslib[crypto,pynacl]>=0.27.0
 attrs
 python-dateutil
 iso8601


Test patch:
diff --git a/tests/demo_dsse_files/demo.layout.template b/tests/demo_dsse_files/demo.layout.template
new file mode 100644
index 000000000..eed5fbb73
--- /dev/null
+++ b/tests/demo_dsse_files/demo.layout.template
@@ -0,0 +1,5 @@
+{
+ "payload": "eyJfdHlwZSI6ImxheW91dCIsImluc3BlY3QiOlt7Il90eXBlIjoiaW5zcGVjdGlvbiIsImV4cGVjdGVkX21hdGVyaWFscyI6W1siTUFUQ0giLCJmb28udGFyLmd6IiwiV0lUSCIsIlBST0RVQ1RTIiwiRlJPTSIsInBhY2thZ2UiXV0sImV4cGVjdGVkX3Byb2R1Y3RzIjpbWyJNQVRDSCIsImZvby5weSIsIldJVEgiLCJQUk9EVUNUUyIsIkZST00iLCJ3cml0ZS1jb2RlIl1dLCJuYW1lIjoidW50YXIiLCJydW4iOlsicHl0aG9uIiwiLi9zY3JpcHRzL3RhciIsInhmeiIsImZvby50YXIuZ3oiXX1dLCJrZXlzIjp7IjJmODliOTI3MmFjZmM4ZjRhMGEwZjA5NGQ3ODlmZGIwYmE3OThiMGZlNDFmMmY1ZjQxN2MxMmYwMDg1ZmY0OTgiOnsia2V5aWQiOiIyZjg5YjkyNzJhY2ZjOGY0YTBhMGYwOTRkNzg5ZmRiMGJhNzk4YjBmZTQxZjJmNWY0MTdjMTJmMDA4NWZmNDk4Iiwia2V5aWRfaGFzaF9hbGdvcml0aG1zIjpbInNoYTI1NiIsInNoYTUxMiJdLCJrZXl0eXBlIjoicnNhIiwia2V5dmFsIjp7InByaXZhdGUiOiIiLCJwdWJsaWMiOiItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUF6Z0xCc01GU2d3QmlXVEJtVnN5V1xuNUtiSndMRlNvZEF6ZFVoVTJCcTZTZFJ6L1c2VU9CR2RvalpYaWJ4dXBqUnRBYUVRVy9lWERlKzFDYktnNkVOWlxuR3QyRDlIR0ZDUVpnUVM4T05nTkRRR2lOeGdBcE1BMFQyMUFhVWhydTB2RW9memROMURmRUY0Q0FHdjVBa2NnS1xuc2FsaFR5T05lcnZGSWpGRWRYR2VsRlo3ZFZNVjNQcDVXa1pQRzBqRlFXam5tRFpoVXJ0U3hFdHFiVmdoYzNrS1xuQVVqOUxsLzNqeWkyd1M5MloxajV1ZU44WDYyaFdYMnhCcVE2blZpT016ZHVqa29pWUNSU3d1TUxScXpXMkNiVFxuTDhoRjErUzVLV0tGenhsNXNDVmZwUGU3VjVIa2dFSGp3Q0lMWFRiQ24yZkNNS2xhU2JKL01HMmxXN3FTWTJSb1xud1ZYV2twMXdEcnNKNklpOWYyZEVydjl2SmVPVlplTzlEc29vUTVFdXpMQ2ZRTEVVNW1uN3VsN2JVN3JGc2I4SlxueFlPZXVka05CYXRuTkNnVk1Ba21EUGlOQTdFMzNibUw1QVJSd1UwaVppY3NxTFFSMzJwbXdkYXA4UGpvZnhxUVxuazdHdHZ6L2lZemFMclp2MzNjRldXVHNFT3FLMWdLcWlnU3FnVzlUMjZ3TzlBZ01CQUFFPVxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tIn0sInNjaGVtZSI6InJzYXNzYS1wc3Mtc2hhMjU2In0sIjc3NmEwMGUyOWYzNTU5ZTAxNDFiM2IwOTZmNjk2YWJjNmNmYjBjNjU3YWI0MGY0NDExMzJiMzQ1YjA4NDUzZjUiOnsia2V5aWQiOiI3NzZhMDBlMjlmMzU1OWUwMTQxYjNiMDk2ZjY5NmFiYzZjZmIwYzY1N2FiNDBmNDQxMTMyYjM0NWIwODQ1M2Y1Iiwia2V5aWRfaGFzaF9hbGdvcml0aG1zIjpbInNoYTI1NiIsInNoYTUxMiJdLCJrZXl0eXBlIjoicnNhIiwia2V5dmFsIjp7InByaXZhdGUiOiIiLCJwdWJsaWMiOiItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUEwWmZ6b25wMy9GU2NhSVArS0t1elxuQitPWk5GcGpiVkdXam0zbGVxbkZxSFlMcXJMY0N3NUtobFhweWNKcW9TdlpCcE8rUEZDa3NVeDhVL3J5a2xIR1xuVm9EaUI4NHBSa3ZadEJvVmFBNGI0SUhESWh6MUs1TnFrSmdpZXlhNGZ3UmVUeG1DVzBhOWdIN0FuRGljSEJDWFxubHpNeHFFZHQ2T0tNVjVnNHlqS2F4ZjhsVzcyTzFnU0k0NkdTSVRvbytaN1VVZ3Mzb2ZhTTVVRkljY3pnQ3BVYVxuNWtFS29jQjZjU1o5VThQS1JMU3MweE8wUk9qcmNPVHNmeE1zOGVWNGJzUkNXWTVtQXExV005RUhEU1Y5V084Z1xucXJSbWFuQzRlbk5xYThqVTRPM3poZ0pWZWdQOUEwMXI5QXdOdDZBcWdQU2lrd2hYTi9QNHYxRk1ZVitSNk4zYlxuUzFsc1ZXUkFud0JxNVJGejV6VnZjWTg4SkVrSGJyY0JxUC9BNDkwOU5YYWUxVk1YbW5vSmI0RXpHQWt5VXlTQlxuYStmSFhBVkpnend5djNJNDhkL09Jakg4TldjVm1NL0RRTDdGdGNKazN0cDBZVWpZNXdOcGNiUVRuTHpVUnRsVVxuc2QrTXRHdXZkbER4VVV2dFVZQ0lWS1JkUzhVelluVFBqSTJ4emVvU0haMlpBZ01CQUFFPVxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tIn0sInNjaGVtZSI6InJzYXNzYS1wc3Mtc2hhMjU2In19LCJyZWFkbWUiOiIiLCJzdGVwcyI6W3siX3R5cGUiOiJzdGVwIiwiZXhwZWN0ZWRfY29tbWFuZCI6W10sImV4cGVjdGVkX21hdGVyaWFscyI6W10sImV4cGVjdGVkX3Byb2R1Y3RzIjpbWyJDUkVBVEUiLCJmb28ucHkiXV0sIm5hbWUiOiJ3cml0ZS1jb2RlIiwicHVia2V5cyI6WyI3NzZhMDBlMjlmMzU1OWUwMTQxYjNiMDk2ZjY5NmFiYzZjZmIwYzY1N2FiNDBmNDQxMTMyYjM0NWIwODQ1M2Y1Il0sInRocmVzaG9sZCI6MX0seyJfdHlwZSI6InN0ZXAiLCJleHBlY3RlZF9jb21tYW5kIjpbInRhciIsInpjdmYiLCJmb28udGFyLmd6IiwiZm9vLnB5Il0sImV4cGVjdGVkX21hdGVyaWFscyI6W1siTUFUQ0giLCJmb28ucHkiLCJXSVRIIiwiUFJPRFVDVFMiLCJGUk9NIiwid3JpdGUtY29kZSJdXSwiZXhwZWN0ZWRfcHJvZHVjdHMiOltbIkNSRUFURSIsImZvby50YXIuZ3oiXSxbIkFMTE9XIiwiZm9vLnB5Il1dLCJuYW1lIjoicGFja2FnZSIsInB1YmtleXMiOlsiMmY4OWI5MjcyYWNmYzhmNGEwYTBmMDk0ZDc4OWZkYjBiYTc5OGIwZmU0MWYyZjVmNDE3YzEyZjAwODVmZjQ5OCJdLCJ0aHJlc2hvbGQiOjF9XX0=",
+ "payloadType": "application/vnd.in-toto+json",
+ "signatures": []
+}
\ No newline at end of file
diff --git a/tests/demo_dsse_files/package.2f89b927.link b/tests/demo_dsse_files/package.2f89b927.link
new file mode 100644
index 000000000..0659f797d
--- /dev/null
+++ b/tests/demo_dsse_files/package.2f89b927.link
@@ -0,0 +1,10 @@
+{
+ "payload": "eyJfdHlwZSI6ImxpbmsiLCJieXByb2R1Y3RzIjp7InJldHVybi12YWx1ZSI6MCwic3RkZXJyIjoiYSBmb28ucHlcbiIsInN0ZG91dCI6IiJ9LCJjb21tYW5kIjpbInRhciIsInpjdmYiLCJmb28udGFyLmd6IiwiZm9vLnB5Il0sImVudmlyb25tZW50Ijp7fSwibWF0ZXJpYWxzIjp7ImZvby5weSI6eyJzaGEyNTYiOiI3NGRjMzcyN2M2ZTg5MzA4YjM5ZTRkZmVkZjc4N2UzNzg0MTE5OGIxZmExNjVhMjdjMDEzNTQ0YTYwNTAyNTQ5In19LCJuYW1lIjoicGFja2FnZSIsInByb2R1Y3RzIjp7ImZvby50YXIuZ3oiOnsic2hhMjU2IjoiNTI5NDdjYjc4YjkxYWQwMWZlODFjZDZhZWY0MmQxZjY4MTdlOTJiOWU2OTM2YzFlNWFhYmI3Yzk4NTE0ZjM1NSJ9fX0=",
+ "payloadType": "application/vnd.in-toto+json",
+ "signatures": [
+  {
+   "keyid": "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498",
+   "sig": "a223bf750849e5f061a7613d35b1bcd61d47f31011bdc3f1bf4b5f6567ad0077e36e1698bdc8027c2cebf0bb5cec9583eb6ec8084602630d7240803d1e4d7ed902a7ca9d0ed288a21ec8fed00361e8c915bcb45147af608e4f47cb32c0d7e702f0e0b3ebffe5b701920ddb34fd7dfa406c6c0cdfacb989f47c21b39a2628ee39993de252cb943dab77436fef48a365538f78ce6c362c0c362fb701fe216ac289eff5e8a3315fb2e94d1b94599ca8c9868a508f71819c57c7b215976e291a02d13de8b1412c88740b302f18a07201df93a6c1eb07c01bb89d6dc2316eb3c04ac87030f13b217a7bd4ce14f0f40ca37d7dc4f7c3587d7dbb3468dc0a366db9d2ae488dc35194edd10b256ff642f2805ad6a34012cb3405be2718052b3422210244b8875de9920a52d068e51e18ecf3869722371325edf0c23638f2827e51906e52611889fe08ca8f98063be80c67a94ce250774bbc0d91564f8efa5979f7caab0c2eceaeec26cf50eb85a101ece178daee5ae18e8a1fd59fbd2aafd5833245d9f7"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/tests/demo_dsse_files/write-code.776a00e2.link b/tests/demo_dsse_files/write-code.776a00e2.link
new file mode 100644
index 000000000..3d66ff10a
--- /dev/null
+++ b/tests/demo_dsse_files/write-code.776a00e2.link
@@ -0,0 +1,10 @@
+{
+ "payload": "eyJfdHlwZSI6ImxpbmsiLCJieXByb2R1Y3RzIjp7fSwiY29tbWFuZCI6W10sImVudmlyb25tZW50Ijp7fSwibWF0ZXJpYWxzIjp7fSwibmFtZSI6IndyaXRlLWNvZGUiLCJwcm9kdWN0cyI6eyJmb28ucHkiOnsic2hhMjU2IjoiNzRkYzM3MjdjNmU4OTMwOGIzOWU0ZGZlZGY3ODdlMzc4NDExOThiMWZhMTY1YTI3YzAxMzU0NGE2MDUwMjU0OSJ9fX0=",
+ "payloadType": "application/vnd.in-toto+json",
+ "signatures": [
+  {
+   "keyid": "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5",
+   "sig": "84eca142f681ab9a49592ee0912fe0632b7a9630bcfdece62b6ba6b5ba328fadf6c568e26c35b1f3201c7ee6689d7c71b80e5126d704e13a5fd2fb98138e535858aaba70941fe75efd1eaa6d99545f1278ba757c2426c40c73f7e2dabd0868ab5482ba48676ea5c0ceda90f7d5cc65c1ecd7f78bebf0085078bf4a0cc7f2a4d1ecda52447613c5ee23472e96a685d51011f9a4d2abb8dae356a20bce4c5030a370622dd800f41db936c5508eab0ed368c257157dbf1312c0139b9599ba757ed8e544b5b18400a5dd621b57c3b3d21fe321a12a0ebaa67e7c53f2b69c523ad884b4fb8448b0075b2d4ddf1f03a8a380423688cf3a351ff245879dd123f50d03854b3ee553f276498f2e0e516c93724e0813cf6a3d5b5058bad35791e261e34cf54fbe94ae933532244d09990bbd72066af4d19a8d3b48c8ff763dc7b4446b32c7a90c85e1fd858cc2f8b5280701e770b3634340058de51280813fd6c268098503337649e90f840e38ab35bb527ae6ab60b8e2e178bdfce31f29f615b0ee696ece"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/tests/models/test_envelope.py b/tests/models/test_envelope.py
new file mode 100644
index 000000000..a8ee2f802
--- /dev/null
+++ b/tests/models/test_envelope.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+# Copyright New York University and the in-toto contributors
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+<Program Name>
+  test_envelope.py
+
+<Author>
+  Lukas Puehringer <lukas.puehringer@nyu.edu>
+
+<Started>
+  Oct 25, 2022
+
+<Copyright>
+  See LICENSE for licensing information.
+
+<Purpose>
+  Test in_toto.models.metadata.Envelope class methods.
+
+"""
+
+import os
+import unittest
+
+from in_toto.models.metadata import Envelope
+from in_toto.models.layout import Layout
+from in_toto.models.link import Link
+
+from tests.common import TmpDirMixin
+
+class TestMetablockValidator(unittest.TestCase, TmpDirMixin):
+  """Test in_toto.models.metadata.Envelope methods. """
+
+  def test_create_envelope(self):
+    """Test DSSE envelope creation from Link or Layout."""
+
+    # Create DSSE Envelope from empty Link.
+    link = Link()
+    env = Envelope.from_signable(link)
+
+    # Verify links.
+    self.assertEqual(env.get_payload(), link)
+
+    # Create DSSE Envelope from empty Layout.
+    layout = Layout()
+    env = Envelope.from_signable(layout)
+
+    # Verify layouts.
+    self.assertEqual(env.get_payload(), layout)
+
+  def test_load_envelope(self):
+    """Test loading and parsing of in-toto envelope."""
+
+    # Demo DSSE Metadata Files
+    demo_dsse_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "../demo_dsse_files")
+
+    layout_path = os.path.join(demo_dsse_files, "demo.layout.template")
+    link_path = os.path.join(demo_dsse_files, "package.2f89b927.link")
+
+    # Load layout metadata.
+    env = Envelope.load(layout_path)
+    layout = env.get_payload()
+
+    self.assertIsInstance(layout, Layout)
+
+    # Load link metadata.
+    env = Envelope.load(link_path)
+    link = env.get_payload()
+
+    self.assertIsInstance(link, Link)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/test_in_toto_record.py b/tests/test_in_toto_record.py
index 3d179e101..55a94fb5b 100644
--- a/tests/test_in_toto_record.py
+++ b/tests/test_in_toto_record.py
@@ -227,5 +227,65 @@ def test_missing_unfinished_link(self):
     self.assert_cli_sys_exit(["stop"] + args, 1)
 
 
+class TestInTotoRecordToolWithDSSE(CliTestCase, TmpDirMixin, GPGKeysMixin, GenKeysMixin):
+  """Test in_toto_record's main() with --use-dsse argument - requires sys.argv
+  patching; and in_toto_record_start/in_toto_record_stop - calls runlib and
+  error logs/exits on Exception. """
+  cli_main_func = staticmethod(in_toto_record_main)
+
+  @classmethod
+  def setUpClass(self):
+    """Create and change into temporary directory,
+    generate key pair, dummy artifact and base arguments. """
+    self.set_up_test_dir()
+    self.set_up_keys()
+
+    self.test_artifact1 = "test_artifact1"
+    self.test_artifact2 = "test_artifact2"
+    Path(self.test_artifact1).touch()
+    Path(self.test_artifact2).touch()
+
+
+  @classmethod
+  def tearDownClass(self):
+    self.tear_down_test_dir()
+
+
+  def test_start_stop(self):
+    """Test CLI command record start/stop with various arguments. """
+
+    # Start/stop recording using rsa key
+    args = ["--step-name", "test1", "--key", self.rsa_key_path, "--use-dsse"]
+    self.assert_cli_sys_exit(["start"] + args, 0)
+    self.assert_cli_sys_exit(["stop"] + args, 0)
+
+    # Start/stop with recording one artifact using rsa key
+    args = ["--step-name", "test2", "--key", self.rsa_key_path, "--use-dsse"]
+    self.assert_cli_sys_exit(["start"] + args + ["--materials",
+      self.test_artifact1], 0)
+    self.assert_cli_sys_exit(["stop"] + args + ["--products",
+      self.test_artifact1], 0)
+
+    # Start/stop with excluding one artifact using rsa key
+    args = ["--step-name", "test2.5", "--key", self.rsa_key_path, "--use-dsse"]
+    self.assert_cli_sys_exit(["start"] + args + ["--materials",
+      self.test_artifact1, "--exclude", "test*"], 0)
+    self.assert_cli_sys_exit(["stop"] + args + ["--products",
+      self.test_artifact1, "--exclude", "test*"], 0)
+
+    # Start/stop with base-path using rsa key
+    args = ["--step-name", "test2.6", "--key", self.rsa_key_path, "--base-path",
+      self.test_dir, "--use-dsse"]
+    self.assert_cli_sys_exit(["start"] + args, 0)
+    self.assert_cli_sys_exit(["stop"] + args, 0)
+
+    # Start/stop with recording multiple artifacts using rsa key
+    args = ["--step-name", "test3", "--key", self.rsa_key_path, "--use-dsse"]
+    self.assert_cli_sys_exit(["start"] + args + ["--materials",
+      self.test_artifact1, self.test_artifact2], 0)
+    self.assert_cli_sys_exit(["stop"] + args + ["--products",
+      self.test_artifact2, self.test_artifact2], 0)
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/tests/test_in_toto_run.py b/tests/test_in_toto_run.py
index ed5c1c523..e7a9acaa0 100755
--- a/tests/test_in_toto_run.py
+++ b/tests/test_in_toto_run.py
@@ -29,7 +29,7 @@
 from pathlib import Path
 from unittest import mock
 
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Metadata, Metablock
 from in_toto.in_toto_run import main as in_toto_run_main
 from in_toto.models.link import FILENAME_FORMAT
 
@@ -275,5 +275,119 @@ def test_main_encrypted_key_but_no_pw(self):
     self.assert_cli_sys_exit(args, 1)
     self.assertFalse(os.path.exists(self.test_link_ed25519_enc))
 
+
+class TestInTotoRunToolWithDSSE(CliTestCase, TmpDirMixin, GPGKeysMixin, GenKeysMixin):
+  """Test in_toto_run's main() with --use-dsse argument - requires sys.argv
+  patching; and in_toto_run- calls runlib and error logs/exits on Exception."""
+  cli_main_func = staticmethod(in_toto_run_main)
+
+  @classmethod
+  def setUpClass(self):
+    """Create and change into temporary directory,
+    generate key pair, dummy artifact and base arguments. """
+    self.set_up_test_dir()
+    self.set_up_gpg_keys()
+    self.set_up_keys()
+
+    self.test_step = "test_step"
+    self.test_link_rsa = FILENAME_FORMAT.format(
+        step_name=self.test_step, keyid=self.rsa_key_id)
+    self.test_link_ed25519 = FILENAME_FORMAT.format(
+        step_name=self.test_step, keyid=self.ed25519_key_id)
+    self.test_link_rsa_enc = FILENAME_FORMAT.format(
+        step_name=self.test_step, keyid=self.rsa_key_enc_id)
+    self.test_link_ed25519_enc = FILENAME_FORMAT.format(
+        step_name=self.test_step, keyid=self.ed25519_key_enc_id)
+
+    self.test_artifact = "test_artifact"
+    Path(self.test_artifact).touch()
+
+  @classmethod
+  def tearDownClass(self):
+    self.tear_down_test_dir()
+
+  def tearDown(self):
+    for link in glob.glob("*.link"):
+      os.remove(link)
+
+  def test_main_required_args(self):
+    """Test CLI command with required arguments. """
+
+    args = ["--step-name", self.test_step, "--key", self.rsa_key_path,
+        "--use-dsse", "--", "python", "--version"]
+
+    self.assert_cli_sys_exit(args, 0)
+    self.assertTrue(os.path.exists(self.test_link_rsa))
+
+
+  def test_main_optional_args(self):
+    """Test CLI command with optional arguments. """
+
+    named_args = ["--step-name", self.test_step, "--key",
+        self.rsa_key_path, "--materials", self.test_artifact, "--products",
+        self.test_artifact, "--record-streams", "--use-dsse"]
+    positional_args = ["--", "python", "--version"]
+
+    # Test and assert recorded artifacts
+    args1 = named_args + positional_args
+    self.assert_cli_sys_exit(args1, 0)
+    metadata = Metadata.load(self.test_link_rsa)
+    link = metadata.get_payload()
+    self.assertTrue(self.test_artifact in
+        list(link.materials.keys()))
+    self.assertTrue(self.test_artifact in
+        list(link.products.keys()))
+
+    # Test and assert exlcuded artifacts
+    args2 = named_args + ["--exclude", "*test*"] + positional_args
+    self.assert_cli_sys_exit(args2, 0)
+    link = Metadata.load(self.test_link_rsa).get_payload()
+    self.assertFalse(link.materials)
+    self.assertFalse(link.products)
+
+    # Test with base path
+    args3 = named_args + ["--base-path", self.test_dir] + positional_args
+    self.assert_cli_sys_exit(args3, 0)
+    link = Metadata.load(self.test_link_rsa).get_payload()
+    self.assertListEqual(list(link.materials.keys()),
+        [self.test_artifact])
+    self.assertListEqual(list(link.products.keys()),
+        [self.test_artifact])
+
+    # Test with bogus base path
+    args4 = named_args + ["--base-path", "bogus/path"] + positional_args
+    self.assert_cli_sys_exit(args4, 1)
+
+    # Test with lstrip path
+    strip_prefix = self.test_artifact[:-1]
+    args5 = named_args + ["--lstrip-paths", strip_prefix] + positional_args
+    self.assert_cli_sys_exit(args5, 0)
+    link = Metadata.load(self.test_link_rsa).get_payload()
+    self.assertListEqual(list(link.materials.keys()),
+        [self.test_artifact[len(strip_prefix):]])
+    self.assertListEqual(list(link.products.keys()),
+        [self.test_artifact[len(strip_prefix):]])
+
+
+  def test_main_with_default_gpg_key(self):
+    """Test CLI command with default gpg key."""
+    args = ["-n", self.test_step,
+            "--gpg", "--gpg-home", self.gnupg_home, "--use-dsse",
+            "--", "python", "--version"]
+
+    self.assert_cli_sys_exit(args, 1)
+
+
+  def test_main_no_command_arg(self):
+    """Test CLI command with --no-command argument. """
+
+    args = ["in_toto_run.py", "--step-name", self.test_step, "--key",
+        self.rsa_key_path, "--no-command", "--use-dsse"]
+
+    self.assert_cli_sys_exit(args, 0)
+
+    self.assertTrue(os.path.exists(self.test_link_rsa))
+
+
 if __name__ == "__main__":
   unittest.main()
diff --git a/tests/test_in_toto_sign.py b/tests/test_in_toto_sign.py
index 5a98b6885..74997b79d 100644
--- a/tests/test_in_toto_sign.py
+++ b/tests/test_in_toto_sign.py
@@ -330,5 +330,146 @@ def test_bad_metadata(self):
         "-k", "key-not-used",
         ], 2)
 
+class TestInTotoSignToolWithDSSE(CliTestCase, TmpDirMixin, GPGKeysMixin, GenKeysMixin):
+  """Test in_toto_sign's main() for dsse metadata files - requires sys.argv
+  patching; error logs/exits on Exception. """
+  cli_main_func = staticmethod(in_toto_sign_main)
+
+  @classmethod
+  def setUpClass(self):
+    # Find demo files
+    demo_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_files")
+
+    # Demo DSSE Metadata Files
+    demo_dsse_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_dsse_files")
+
+    # Create and change into temporary directory
+    self.set_up_test_dir()
+    self.set_up_gpg_keys()
+    self.set_up_keys()
+
+    # Copy demo files to temp dir
+    for file_path in os.listdir(demo_files):
+      shutil.copy(os.path.join(demo_files, file_path), self.test_dir)
+
+    # Copy demo DSSE files to temp dir
+    for file_path in os.listdir(demo_dsse_files):
+      shutil.copy(os.path.join(demo_dsse_files, file_path), self.test_dir)
+
+    self.layout_path = "demo.layout.template"
+    self.link_path = "package.2f89b927.link"
+    self.alice_path = "alice"
+    self.alice_pub_path = "alice.pub"
+    self.bob_path = "bob"
+    self.bob_pub_path = "bob.pub"
+    self.carl_path = "carl"
+    self.carl_pub_path = "carl.pub"
+    self.danny_path = "danny"
+    self.danny_pub_path = "danny.pub"
+
+  @classmethod
+  def tearDownClass(self):
+    self.tear_down_test_dir()
+
+
+  def test_sign_and_verify(self):
+    """Test signing and verifying Layout and Link metadata with
+    different combinations of arguments. """
+
+    # Sign Layout with multiple keys and write to "tmp.layout"
+    self.assert_cli_sys_exit([
+        "-f", self.layout_path,
+        "-k", self.alice_path, self.bob_path,
+        "-o", "tmp.layout",
+        ], 0)
+
+    # Verify "tmp.layout" (requires all keys)
+    self.assert_cli_sys_exit([
+        "-f", "tmp.layout",
+        "-k", self.alice_pub_path, self.bob_pub_path,
+        "--verify",
+        ], 0)
+
+    # Sign Layout "tmp.layout", appending new signature, write to "tmp.layout"
+    self.assert_cli_sys_exit([
+        "-f", "tmp.layout",
+        "-k", self.carl_path,
+        "-a"
+        ], 0)
+
+    # Verify "tmp.layout" (has three signatures now)
+    self.assert_cli_sys_exit([
+        "-f", "tmp.layout",
+        "-k", self.alice_pub_path, self.bob_pub_path, self.carl_pub_path,
+        "--verify"
+        ], 0)
+
+    # Sign Layout "tmp.layout" with ed25519 key, appending new signature,
+    # write to "tmp.layout"
+    self.assert_cli_sys_exit([
+        "-f", "tmp.layout",
+        "-k", self.danny_path,
+        "-t", "ed25519",
+        "-a"
+        ], 0)
+
+    # Verify "tmp.layout" (has four signatures now)
+    self.assert_cli_sys_exit([
+        "-f", "tmp.layout",
+        "-k", self.alice_pub_path, self.bob_pub_path, self.carl_pub_path,
+        self.danny_pub_path,
+        "-t", "rsa", "rsa", "rsa", "ed25519",
+        "--verify"
+        ], 0)
+
+    # Sign Link, replacing old signature
+    # and write to same file as input
+    self.assert_cli_sys_exit([
+        "-f", self.link_path,
+        "-k", self.bob_path,
+        "-o", self.link_path,
+        ], 0)
+
+    # Verify Link
+    self.assert_cli_sys_exit([
+        "-f", self.link_path,
+        "-k", self.bob_pub_path,
+        "--verify"
+        ], 0)
+
+    # Replace signature to Link and store to new file using passed
+    # key's (alice) id as infix
+    self.assert_cli_sys_exit([
+        "-f", self.link_path,
+        "-k", self.alice_path
+        ], 0)
+    # Verify Link with alice's keyid as infix
+    self.assert_cli_sys_exit([
+        "-f", "package.556caebd.link",
+        "-k", self.alice_pub_path,
+        "--verify"
+        ], 0)
+
+
+  def test_fail_verification(self):
+    """Fail signature verification. """
+    # Fail with wrong key (not used for signing)
+    self.assert_cli_sys_exit([
+        "-f", self.layout_path,
+        "-k", self.carl_pub_path,
+        "--verify"
+        ], 1)
+
+    # Fail with wrong gpg keyid (not used for signing)
+    self.assert_cli_sys_exit([
+        "-f", self.layout_path,
+        "-g", self.gpg_key_0C8A17,
+        "--gpg-home", self.gnupg_home,
+        "--verify"
+        ], 2)
+
+
 if __name__ == "__main__":
   unittest.main()
diff --git a/tests/test_in_toto_verify.py b/tests/test_in_toto_verify.py
index 834f9f9a8..78b0b1d26 100755
--- a/tests/test_in_toto_verify.py
+++ b/tests/test_in_toto_verify.py
@@ -25,11 +25,12 @@
 import unittest
 import shutil
 
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Metadata
 from in_toto.in_toto_verify import main as in_toto_verify_main
 from securesystemslib.interface import (import_rsa_privatekey_from_file,
     import_ed25519_privatekey_from_file)
 from securesystemslib.gpg.constants import have_gpg
+from securesystemslib.signer import SSlibSigner
 
 from tests.common import CliTestCase, TmpDirMixin, GPGKeysMixin
 
@@ -80,7 +81,7 @@ def setUpClass(self):
     shutil.copytree(scripts_directory, 'scripts')
 
     # Load layout template
-    layout_template = Metablock.load("demo.layout.template")
+    layout_template = Metadata.load("demo.layout.template")
 
     # Store layout paths to be used in tests
     self.layout_single_signed_path = "single-signed.layout"
@@ -151,6 +152,113 @@ def test_main_link_dir(self):
     self.assert_cli_sys_exit(args, 1)
 
 
+class TestInTotoVerifyToolWithDSSE(CliTestCase, TmpDirMixin):
+  """
+  Tests
+    - in_toto_verify's main() - requires sys.argv patching;
+    - in_toto_verify - calls verifylib.in_toto_verify and error logs/exits
+      in case of a raised Exception.
+
+  Uses in-toto demo supply chain link metadata files and basic layout for
+  verification:
+
+  Copies the basic layout for different test scenarios:
+    - signed layout
+    - multiple signed layout (using two project owner keys)
+  """
+  cli_main_func = staticmethod(in_toto_verify_main)
+
+
+  @classmethod
+  def setUpClass(self):
+    """Creates and changes into temporary directory.
+    Copies demo files to temp dir...
+      - owner/functionary key pairs
+      - *.link metadata files
+      - layout template (not signed, no expiration date)
+      - final product
+
+    ...and dumps various layouts for different test scenarios
+    """
+
+    # Find demo files
+    demo_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_files")
+
+    # Demo DSSE Metadata Files
+    demo_dsse_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_dsse_files")
+
+    # find where the scripts directory is located.
+    scripts_directory = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "scripts")
+
+    self.set_up_test_dir()
+
+    # Copy demo files to temp dir
+    for fn in os.listdir(demo_files):
+      shutil.copy(os.path.join(demo_files, fn), self.test_dir)
+
+    for fn in os.listdir(demo_dsse_files):
+      shutil.copy(os.path.join(demo_dsse_files, fn), self.test_dir)
+
+    shutil.copytree(scripts_directory, 'scripts')
+
+    # Load layout template
+    layout_template = Metadata.load("demo.layout.template")
+
+    # Store layout paths to be used in tests
+    self.layout_single_signed_path = "single-signed.layout"
+    self.layout_double_signed_path = "double-signed.layout"
+
+    # Import layout signing keys
+    alice = import_rsa_privatekey_from_file("alice")
+    bob = import_rsa_privatekey_from_file("bob")
+    self.alice_path = "alice.pub"
+    self.bob_path = "bob.pub"
+
+    # dump a single signed layout
+    layout_template.create_signature(SSlibSigner(alice))
+    layout_template.dump(self.layout_single_signed_path)
+    # dump a double signed layout
+    layout_template.create_signature(SSlibSigner(bob))
+    layout_template.dump(self.layout_double_signed_path)
+
+
+  @classmethod
+  def tearDownClass(self):
+    self.tear_down_test_dir()
+
+
+  def test_main_required_args(self):
+    """Test in-toto-verify CLI tool with required arguments. """
+    args = ["--layout", self.layout_single_signed_path,
+        "--layout-keys", self.alice_path]
+
+    self.assert_cli_sys_exit(args, 0)
+
+
+  def test_main_multiple_keys(self):
+    """Test in-toto-verify CLI tool with multiple keys. """
+    args = ["--layout", self.layout_double_signed_path,
+        "--layout-keys", self.alice_path, self.bob_path]
+    self.assert_cli_sys_exit(args, 0)
+
+
+  def test_main_link_dir(self):
+    """Test in-toto-verify CLI tool with explicit link dir. """
+
+    # Use current working directory explicitly to load links
+    args = ["--layout", self.layout_single_signed_path,
+        "--layout-keys", self.alice_path, "--link-dir", "."]
+    self.assert_cli_sys_exit(args, 0)
+
+    # Fail with an explicit link directory, where no links are found
+    args = ["--layout", self.layout_single_signed_path,
+        "--layout-keys", self.alice_path, "--link-dir", "bad-link-dir"]
+    self.assert_cli_sys_exit(args, 1)
+
+
 
 class TestInTotoVerifyToolMixedKeys(CliTestCase, TmpDirMixin):
   """ Tests in-toto-verify like TestInTotoVerifyTool but with
@@ -185,7 +293,7 @@ def setUpClass(self):
     shutil.copytree(scripts_directory, 'scripts')
 
     # Load layout template
-    layout_template = Metablock.load("demo.layout.template")
+    layout_template = Metadata.load("demo.layout.template")
 
     # Store layout paths to be used in tests
     self.layout_double_signed_path = "double-signed.layout"
@@ -214,6 +322,76 @@ def test_main_multiple_keys(self):
     self.assert_cli_sys_exit(args, 0)
 
 
+class TestInTotoVerifyToolMixedKeysWithDSSE(CliTestCase, TmpDirMixin):
+  """ Tests in-toto-verify like TestInTotoVerifyTool but with
+  both rsa and ed25519 project owner and functionary keys. """
+  cli_main_func = staticmethod(in_toto_verify_main)
+
+
+  @classmethod
+  def setUpClass(self):
+    """Creates and changes into temporary directory.
+    Copies demo files to temp dir...
+      - owner/functionary key pairs
+      - *.link metadata files
+      - layout template (not signed, no expiration date)
+      - final product
+
+    ...and dumps layout for test scenario
+    """
+    # Find demo files
+    demo_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_files")
+
+    # Demo DSSE Metadata Files
+    demo_dsse_files = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "demo_dsse_files")
+
+    # find where the scripts directory is located.
+    scripts_directory = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "scripts")
+
+    self.set_up_test_dir()
+
+    # Copy demo files to temp dir
+    for fn in os.listdir(demo_files):
+      shutil.copy(os.path.join(demo_files, fn), self.test_dir)
+
+    for fn in os.listdir(demo_dsse_files):
+      shutil.copy(os.path.join(demo_dsse_files, fn), self.test_dir)
+
+    shutil.copytree(scripts_directory, 'scripts')
+
+    # Load layout template
+    layout_template = Metadata.load("demo.layout.template")
+
+    # Store layout paths to be used in tests
+    self.layout_double_signed_path = "double-signed.layout"
+
+    # Import layout signing keys
+    alice = import_rsa_privatekey_from_file("alice")
+    danny = import_ed25519_privatekey_from_file("danny")
+    self.alice_path = "alice.pub"
+    self.danny_path = "danny.pub"
+
+    # dump a double signed layout
+    layout_template.create_signature(SSlibSigner(alice))
+    layout_template.create_signature(SSlibSigner(danny))
+    layout_template.dump(self.layout_double_signed_path)
+
+
+  @classmethod
+  def tearDownClass(self):
+    self.tear_down_test_dir()
+
+  def test_main_multiple_keys(self):
+    """Test in-toto-verify CLI tool with multiple keys. """
+    args = ["--layout", self.layout_double_signed_path,
+       "--layout-keys", self.alice_path, self.danny_path,
+       "--key-types", "rsa", "ed25519"]
+    self.assert_cli_sys_exit(args, 0)
+
+
 @unittest.skipIf(not have_gpg(), "gpg not found")
 class TestInTotoVerifyToolGPG(CliTestCase, TmpDirMixin, GPGKeysMixin):
   """ Tests in-toto-verify like TestInTotoVerifyTool but with
@@ -243,7 +421,7 @@ def setUpClass(self):
     shutil.copytree(scripts_directory, 'scripts')
 
     # Sign layout template with gpg key
-    layout_template = Metablock.load("demo.layout.template")
+    layout_template = Metadata.load("demo.layout.template")
 
     self.layout_path = "gpg_signed.layout"
     layout_template.sign_gpg(self.gpg_key_0C8A17, self.gnupg_home)
diff --git a/tests/test_param_substitution.py b/tests/test_param_substitution.py
index c95a73e08..b51532811 100644
--- a/tests/test_param_substitution.py
+++ b/tests/test_param_substitution.py
@@ -28,12 +28,13 @@
 import unittest
 
 import in_toto.settings
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Envelope, Metablock
 from in_toto.models.layout import  Layout
 from in_toto.verifylib import in_toto_verify, substitute_parameters
 from securesystemslib.interface import (
     import_rsa_privatekey_from_file,
     import_publickeys_from_file)
+from securesystemslib.signer import SSlibSigner
 
 import in_toto.exceptions
 
@@ -191,6 +192,20 @@ def test_substitute(self):
 
     self.assertEqual(self.layout.steps[0].expected_command[0], "vim")
 
+  def test_substitute_for_envelope(self):
+    """Do a simple substitution on the expected_command field for DSSE
+    envelope."""
+    signed_layout = Envelope.from_signable(self.layout)
+    signed_layout.create_signature(SSlibSigner(self.alice))
+
+    # we will catch a LinkNotFound error because we don't have (and don't need)
+    # the metadata.
+    with self.assertRaises(in_toto.exceptions.LinkNotFoundError):
+      in_toto_verify(signed_layout, self.alice_pub_dict,
+          substitution_parameters={"EDITOR":"vim"})
+
+    self.assertEqual(self.layout.steps[0].expected_command[0], "vim")
+
 
 if __name__ == "__main__":
   unittest.main()
diff --git a/tests/test_runlib.py b/tests/test_runlib.py
index 7c72a74da..ec9bc0de7 100755
--- a/tests/test_runlib.py
+++ b/tests/test_runlib.py
@@ -31,7 +31,7 @@
 
 import in_toto.settings
 import in_toto.exceptions
-from in_toto.models.metadata import Metablock
+from in_toto.models.metadata import Envelope, Metablock
 from in_toto.exceptions import SignatureVerificationError
 from in_toto.runlib import (in_toto_run, in_toto_record_start,
     in_toto_record_stop, record_artifacts_as_dict, _apply_exclude_patterns,
@@ -772,6 +772,14 @@ def test_in_toto_read_only_metadata_directory(self):
           True, self.key, metadata_directory=tmp_dir)
     os.rmdir(tmp_dir)
 
+  def test_in_toto_for_dsse(self):
+    """Test metadata generation using dsse."""
+
+    link_metadata = in_toto_run(self.step_name, None, None,
+        ["python", "--version"], True, self.key, use_dsse=True)
+    self.assertIsInstance(link_metadata, Envelope)
+    link_metadata.verify_signature(self.key)
+
 
 class TestInTotoRecordStart(unittest.TestCase, TmpDirMixin):
   """"Test in_toto_record_start(step_name, key, material_list). """
@@ -809,7 +817,7 @@ def test_create_unfinished_metadata_with_expected_material(self):
     self.assertEqual(list(link.signed.materials.keys()), [self.test_material])
     os.remove(self.link_name_unfinished)
 
-  def test_create_unfininished_metadata_verify_signature(self):
+  def test_create_unfinished_metadata_verify_signature(self):
     """Test record start creates metadata with expected signature. """
     in_toto_record_start(
         self.step_name, [self.test_material], self.key)
@@ -824,6 +832,14 @@ def test_no_key_arguments(self):
           self.step_name, [], signing_key=None, gpg_keyid=None,
           gpg_use_default=False)
 
+  def test_create_unfinished_metadata_using_dsse(self):
+    """Test record start creates metadata using dsse."""
+    in_toto_record_start(
+        self.step_name, [self.test_material], self.key, use_dsse=True)
+    link_metadata = Envelope.load(self.link_name_unfinished)
+    link_metadata.verify_signature(self.key)
+    os.remove(self.link_name_unfinished)
+
 class TestInTotoRecordStop(unittest.TestCase, TmpDirMixin):
   """"Test in_toto_record_stop(step_name, key, product_list). """
 
@@ -995,6 +1011,18 @@ def test_read_only_metadata_directory(self):
           metadata_directory=tmp_dir)
     os.rmdir(tmp_dir)
 
+  def test_created_metadata_using_dsse(self):
+    """Test record stop records created metadata with dsse."""
+    in_toto_record_start(self.step_name, [], self.key, use_dsse=True)
+    in_toto_record_stop(self.step_name, [self.test_product], self.key)
+
+    link_metadata = Envelope.load(self.link_name)
+    link_metadata.verify_signature(self.key)
+
+    link = link_metadata.get_payload()
+    self.assertEqual(list(link.products.keys()), [self.test_product])
+    os.remove(self.link_name)
+
 
 if __name__ == "__main__":
   unittest.main()
diff --git a/tests/test_verifylib.py b/tests/test_verifylib.py
index 1ea942296..3b148fb87 100644
--- a/tests/test_verifylib.py
+++ b/tests/test_verifylib.py
@@ -21,46 +21,42 @@
 
 """
 
+import copy
+import glob
 import os
+import shlex
 import shutil
-import copy
 import tempfile
 import unittest
-import glob
-import shlex
-
+from datetime import datetime
 from unittest.mock import patch
 
-from datetime import datetime
+import securesystemslib.exceptions
+import securesystemslib.gpg.functions
 from dateutil.relativedelta import relativedelta
+from securesystemslib.interface import (import_publickeys_from_file,
+    import_rsa_privatekey_from_file, import_rsa_publickey_from_file)
 
+import in_toto.exceptions
 import in_toto.settings
+from in_toto.exceptions import (BadReturnValueError, LayoutExpiredError,
+    RuleVerificationError, ThresholdVerificationError,
+    SignatureVerificationError)
+from in_toto.models.layout import (SUBLAYOUT_LINK_DIR_FORMAT, Inspection,
+    Layout, Step)
+from in_toto.models.link import FILENAME_FORMAT, Link
 from in_toto.models.metadata import Metablock
-from in_toto.models.link import Link, FILENAME_FORMAT
-from in_toto.models.layout import (Step, Inspection, Layout,
-    SUBLAYOUT_LINK_DIR_FORMAT)
-from in_toto.verifylib import (verify_delete_rule, verify_create_rule,
-    verify_modify_rule, verify_allow_rule, verify_disallow_rule,
-    verify_require_rule, verify_match_rule, verify_item_rules,
-    verify_all_item_rules, verify_command_alignment, run_all_inspections,
-    in_toto_verify, verify_sublayouts, get_summary_link, _raise_on_bad_retval,
-    load_links_for_layout, verify_link_signature_thresholds,
-    verify_threshold_constraints)
-from in_toto.exceptions import (RuleVerificationError,
-    SignatureVerificationError, LayoutExpiredError, BadReturnValueError,
-    ThresholdVerificationError)
-from securesystemslib.interface import (
-    import_rsa_privatekey_from_file,
-    import_rsa_publickey_from_file,
-    import_publickeys_from_file)
 from in_toto.rulelib import unpack_rule
-import securesystemslib.gpg.functions
 from securesystemslib.gpg.constants import have_gpg
 
-import securesystemslib.exceptions
-import in_toto.exceptions
-
-from tests.common import TmpDirMixin, GPGKeysMixin
+from in_toto.verifylib import (_raise_on_bad_retval, get_summary_link,
+    in_toto_verify, load_links_for_layout, run_all_inspections,
+    verify_all_item_rules, verify_allow_rule, verify_command_alignment,
+    verify_create_rule, verify_delete_rule, verify_disallow_rule,
+    verify_item_rules, verify_link_signature_thresholds, verify_match_rule,
+    verify_modify_rule, verify_require_rule, verify_sublayouts,
+    verify_threshold_constraints)
+from tests.common import GPGKeysMixin, TmpDirMixin
 
 
 class Test_RaiseOnBadRetval(unittest.TestCase):
@@ -414,10 +410,11 @@ def setUp(self):
     }
 
     self.links = {
-        "dest-item": Metablock(signed=Link(
+        "dest-item": Link(
             name="dest-item",
             materials=self.materials,
-            products=self.products)),
+            products=self.products
+          ),
     }
 
 
@@ -604,21 +601,20 @@ def setUp(self):
         "cfdaaf1ab2e4661952a9dec5e8fa3c360c1b06b1a073e8493a7c46d2af8c504b"
 
     self.links = {
-      "item": Metablock(signed=Link(name="item",
-          materials={
-              "foo": {"sha256": self.sha256_1},
-              "foobar": {"sha256": self.sha256_1},
-              "bar": {"sha256": self.sha256_1},
-              "foobarbaz": {"sha256": self.sha256_1}
-          },
-          products={
-              "baz" : {"sha256": self.sha256_1},
-              "foo": {"sha256": self.sha256_1},
-              "bar": {"sha256": self.sha256_2},
-              "foobarbaz": {"sha256": self.sha256_1}
-
-          }
-      ))
+      "item": Link(name="item",
+        materials={
+            "foo": {"sha256": self.sha256_1},
+            "foobar": {"sha256": self.sha256_1},
+            "bar": {"sha256": self.sha256_1},
+            "foobarbaz": {"sha256": self.sha256_1}
+        },
+        products={
+            "baz" : {"sha256": self.sha256_1},
+            "foo": {"sha256": self.sha256_1},
+            "bar": {"sha256": self.sha256_2},
+            "foobarbaz": {"sha256": self.sha256_1}
+        }
+      )
     }
 
   def test_pass_rules_with_each_rule_type(self):
@@ -704,37 +700,37 @@ def setUp(self):
     ]
 
     self.links = {
-      "write-code" : Metablock(signed=Link(name="write-code",
-          products={
-              "foo": {
-                  "sha256": self.sha256_foo
-              }
-          }
-      )),
-      "package" : Metablock(signed=Link(name="package",
+      "write-code" : Link(name="write-code",
+        products={
+            "foo": {
+                "sha256": self.sha256_foo
+            }
+        }
+      ),
+      "package" : Link(name="package",
+        materials={
+            "foo": {
+                "sha256": self.sha256_foo
+            }
+        },
+        products={
+            "foo.tar.gz": {
+                "sha256": self.sha256_foo_tar
+            }
+        }
+      ),
+        "untar" : Link(name="untar",
           materials={
-              "foo": {
-                  "sha256": self.sha256_foo
-              }
-          },
-          products={
               "foo.tar.gz": {
                   "sha256": self.sha256_foo_tar
               }
+          },
+          products={
+              "dir/foo": {
+                  "sha256": self.sha256_foo
+              },
           }
-      )),
-        "untar" : Metablock(signed=Link(name="untar",
-            materials={
-                "foo.tar.gz": {
-                    "sha256": self.sha256_foo_tar
-                }
-            },
-            products={
-                "dir/foo": {
-                    "sha256": self.sha256_foo
-                },
-            }
-        ))
+        )
     }
 
   def test_pass_verify_all_step_rules(self):
@@ -949,7 +945,7 @@ def test_verify_layout_signatures_fail_with_malformed_signature(self):
     """Layout signature verification fails with malformed signatures. """
     layout_metablock = Metablock(signed=Layout())
     signature = layout_metablock.sign(self.alice)
-    pubkey = self.alice
+    pubkey = copy.deepcopy(self.alice)
     pubkey["keyval"]["private"] = ""
 
     del signature["sig"]
@@ -960,7 +956,6 @@ def test_verify_layout_signatures_fail_with_malformed_signature(self):
 
 
 
-
 class TestInTotoVerifyThresholds(unittest.TestCase):
   """Test verifylib functions related to signature thresholds.
 
@@ -1135,16 +1130,14 @@ def test_threshold_constraints_fail_with_unequal_links(self):
     """ Test that the links for a step recorded the same artifacts. """
     # Layout with one step and threshold 2
     layout = Layout(steps=[Step(name=self.name, threshold=2)])
-    link_bob = Metablock(
-        signed=Link(
-          name=self.name,
-          materials={
-            "foo": {"sha256": self.foo_hash}
-          }
-        )
-      )
+    link_bob = Link(
+      name=self.name,
+      materials={
+        "foo": {"sha256": self.foo_hash}
+      }
+    )
     # Cf. signing comment in test_thresholds_constraints_with_not_enough_links
-    link_alice = Metablock(signed=Link(name=self.name))
+    link_alice = Link(name=self.name)
 
     chain_link_dict = {
       self.name: {
@@ -1164,22 +1157,18 @@ def test_threshold_constraints_pas_with_equal_links(self):
     layout = Layout(steps=[Step(name=self.name, threshold=2)])
     # Two authorized links with equal artifact recordings (materials)
     # Cf. signing comment in test_thresholds_constraints_with_not_enough_links
-    link_bob = Metablock(
-        signed=Link(
-          name=self.name,
-          materials={
-            "foo": {"sha256": self.foo_hash}
-          }
-        )
-      )
-    link_alice = Metablock(
-        signed=Link(
-          name=self.name,
-          materials={
-            "foo": {"sha256": self.foo_hash}
-          }
-        )
-      )
+    link_bob = Link(
+      name=self.name,
+      materials={
+        "foo": {"sha256": self.foo_hash}
+      }
+    )
+    link_alice = Link(
+      name=self.name,
+      materials={
+        "foo": {"sha256": self.foo_hash}
+      }
+    )
 
     chain_link_dict = {
       self.name: {
@@ -1685,8 +1674,8 @@ def setUpClass(self):
     self.code_link = Metablock.load("package.2f89b927.link")
     self.package_link = Metablock.load("write-code.776a00e2.link")
     self.demo_links = {
-        "write-code": self.code_link,
-        "package": self.package_link
+        "write-code": self.code_link.signed,
+        "package": self.package_link.signed
       }
 
   @classmethod
@@ -1697,14 +1686,14 @@ def test_get_summary_link_from_demo_layout(self):
     """Create summary link from demo link files and compare properties. """
     sum_link = get_summary_link(self.demo_layout.signed, self.demo_links, "")
 
-    self.assertEqual(sum_link.signed._type, self.code_link.signed._type)
-    self.assertEqual(sum_link.signed.name, "")
-    self.assertEqual(sum_link.signed.materials, self.code_link.signed.materials)
+    self.assertEqual(sum_link._type, self.code_link.signed._type)
+    self.assertEqual(sum_link.name, "")
+    self.assertEqual(sum_link.materials, self.code_link.signed.materials)
 
-    self.assertEqual(sum_link.signed.products, self.package_link.signed.products)
-    self.assertEqual(sum_link.signed.command, self.package_link.signed.command)
-    self.assertEqual(sum_link.signed.byproducts, self.package_link.signed.byproducts)
-    self.assertEqual(sum_link.signed.byproducts.get("return-value"),
+    self.assertEqual(sum_link.products, self.package_link.signed.products)
+    self.assertEqual(sum_link.command, self.package_link.signed.command)
+    self.assertEqual(sum_link.byproducts, self.package_link.signed.byproducts)
+    self.assertEqual(sum_link.byproducts.get("return-value"),
         self.package_link.signed.byproducts.get("return-value"))