CINXE.COM

PEP 740 – Index support for digital attestations | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 740 – Index support for digital attestations | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0740/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 740 – Index support for digital attestations | peps.python.org'> <meta property="og:description" content="This PEP proposes a collection of changes related to the upload and distribution of digitally signed attestations and metadata used to verify them on a Python package repository, such as PyPI."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0740/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP proposes a collection of changes related to the upload and distribution of digitally signed attestations and metadata used to verify them on a Python package repository, such as PyPI."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 740</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 740 – Index support for digital attestations</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">William Woodruff &lt;william&#32;&#97;t&#32;yossarian.net&gt;, Facundo Tuesca &lt;facundo.tuesca&#32;&#97;t&#32;trailofbits.com&gt;, Dustin Ingram &lt;di&#32;&#97;t&#32;python.org&gt;</dd> <dt class="field-even">Sponsor<span class="colon">:</span></dt> <dd class="field-even">Donald Stufft &lt;donald&#32;&#97;t&#32;stufft.io&gt;</dd> <dt class="field-odd">PEP-Delegate<span class="colon">:</span></dt> <dd class="field-odd">Donald Stufft &lt;donald&#32;&#97;t&#32;stufft.io&gt;</dd> <dt class="field-even">Discussions-To<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-740-index-support-for-digital-attestations/44498">Discourse thread</a></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Provisionally accepted but additional feedback needed">Provisional</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-odd">Topic<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="../topic/packaging/">Packaging</a></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">08-Jan-2024</dd> <dt class="field-odd">Post-History<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pre-pep-exposing-trusted-publisher-provenance-on-pypi/42337" title="Discourse thread">02-Jan-2024</a>, <a class="reference external" href="https://discuss.python.org/t/pep-740-index-support-for-digital-attestations/44498" title="Discourse thread">29-Jan-2024</a></dd> <dt class="field-even">Resolution<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-740-index-support-for-digital-attestations/44498/26">Discourse message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale-and-motivation">Rationale and Motivation</a><ul> <li><a class="reference internal" href="#design-considerations">Design Considerations</a></li> <li><a class="reference internal" href="#previous-work">Previous Work</a><ul> <li><a class="reference internal" href="#pgp-signatures">PGP signatures</a></li> <li><a class="reference internal" href="#wheel-signatures">Wheel signatures</a></li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#upload-endpoint-changes">Upload endpoint changes</a></li> <li><a class="reference internal" href="#index-changes">Index changes</a><ul> <li><a class="reference internal" href="#simple-index">Simple Index</a></li> <li><a class="reference internal" href="#json-based-simple-api">JSON-based Simple API</a></li> </ul> </li> <li><a class="reference internal" href="#attestation-objects">Attestation objects</a><ul> <li><a class="reference internal" href="#attestation-statement-and-signature-generation">Attestation statement and signature generation</a></li> </ul> </li> <li><a class="reference internal" href="#provenance-objects">Provenance objects</a><ul> <li><a class="reference internal" href="#changes-to-provenance-objects">Changes to provenance objects</a></li> </ul> </li> <li><a class="reference internal" href="#attestation-verification">Attestation verification</a></li> </ul> </li> <li><a class="reference internal" href="#security-implications">Security Implications</a><ul> <li><a class="reference internal" href="#cryptographic-agility-in-attestations">Cryptographic agility in attestations</a></li> <li><a class="reference internal" href="#index-trust">Index trust</a></li> </ul> </li> <li><a class="reference internal" href="#recommendations">Recommendations</a></li> <li><a class="reference internal" href="#appendix-1-example-trusted-publisher-representation">Appendix 1: Example Trusted Publisher Representation</a></li> <li><a class="reference internal" href="#appendix-2-data-models-for-transparency-log-entries">Appendix 2: Data models for Transparency Log Entries</a></li> <li><a class="reference internal" href="#appendix-3-simple-json-api-size-considerations">Appendix 3: Simple JSON API size considerations</a></li> <li><a class="reference internal" href="#appendix-4-example-attestation-statement">Appendix 4: Example attestation statement</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes a collection of changes related to the upload and distribution of digitally signed attestations and metadata used to verify them on a Python package repository, such as PyPI.</p> <p>These changes have two subcomponents:</p> <ul class="simple"> <li>Changes to the currently unstandardized PyPI upload API, allowing clients to upload digital attestations as <a class="reference internal" href="#attestation-object"><span class="std std-ref">attestation objects</span></a>;</li> <li>Changes to the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#simple-repository-api" title="(in Python Packaging User Guide)"><span class="xref std std-ref">HTML and JSON “simple” APIs</span></a>, allowing clients to retrieve both digital attestations and <a class="reference external" href="https://docs.pypi.org/trusted-publishers/">Trusted Publishing</a> metadata for individual release files as <a class="reference internal" href="#provenance-object"><span class="std std-ref">provenance objects</span></a>.</li> </ul> <p>This PEP does not make a policy recommendation around mandatory digital attestations on release uploads or their subsequent verification by installing clients like <code class="docutils literal notranslate"><span class="pre">pip</span></code>.</p> </section> <section id="rationale-and-motivation"> <h2><a class="toc-backref" href="#rationale-and-motivation" role="doc-backlink">Rationale and Motivation</a></h2> <p>Desire for digital signatures on Python packages has been repeatedly expressed by both package maintainers and downstream users:</p> <ul class="simple"> <li>Maintainers wish to demonstrate the integrity and authenticity of their package uploads;</li> <li>Individual downstream users wish to verify package integrity and authenticity without placing additional trust in their index’s honesty;</li> <li>“Bulk” downstream users (such as Operating System distributions) wish to perform similar verifications and potentially re-expose or countersign for their own downstream packaging ecosystems.</li> </ul> <p>This proposal seeks to accommodate each of the above use cases.</p> <p>Additionally, this proposal identifies the following motivations:</p> <ul> <li>Verifiable provenance for Python package distributions: many Python packages currently contain <em>unauthenticated</em> provenance metadata, such as URLs for source hosts. A cryptographic attestation format could enable strong <em>authenticated</em> links between these packages and their source hosts, allowing both the index and downstream users to cryptographically verify that a package originates from its claimed source repository.</li> <li>Raising attacker requirements: an attacker who seeks to take over a Python package can be described along <em>sophistication</em> (unsophisticated to sophisticated) and <em>targeting</em> dimensions (opportunistic to targeted).<p>Digital attestations impose additional sophistication requirements: the attacker must be sufficiently sophisticated to access private signing material (or signing identities).</p> </li> <li>Index verifiability: in the status quo, the only attestation provided by the index is an optional PGP signature per release file (see <a class="reference internal" href="#pgp-signatures"><span class="std std-ref">PGP signatures</span></a>). These signatures are not (and cannot be) checked by the index either for well-formedness or for validity, since the index has no mechanism for identifying the right public key for the signature. This PEP overcomes this limitation by ensuring that <a class="reference internal" href="#provenance-object"><span class="std std-ref">provenance objects</span></a> contain all of the metadata needed by the index to verify an attestation’s validity.</li> </ul> <p>This PEP proposes a generic attestation format, containing an <a class="reference internal" href="#payload-and-signature-generation"><span class="std std-ref">attestation statement for signature generation</span></a>, with the expectation that index providers adopt the format with a suitable source of identity for signature verification, such as Trusted Publishing.</p> <section id="design-considerations"> <h3><a class="toc-backref" href="#design-considerations" role="doc-backlink">Design Considerations</a></h3> <p>This PEP identifies the following design considerations when evaluating both its own proposed changes and previous work in the same or adjacent areas of Python packaging:</p> <ol class="arabic"> <li>Index accessibility: digital attestations for Python packages are ideally retrievable directly from the index itself, as “detached” resources.<p>This both simplifies some compatibility concerns (by avoiding the need to modify the distribution formats themselves) and also simplifies the behavior of potential installing clients (by allowing them to retrieve each attestation before its corresponding package without needing to do streaming decompression).</p> </li> <li>Verification by the index itself: in addition to enabling verification by installing clients, each digital attestation is <em>ideally</em> verifiable in some form by the index itself.<p>This both increases the overall quality of attestations uploaded to the index (preventing, for example, users from accidentally uploading incorrect or invalid attestations) and also enables UI and UX refinements on the index itself (such as a “provenance” view for each uploaded package).</p> </li> <li>General applicability: digital attestations should be applicable to <em>any and every</em> package uploaded to the index, regardless of its format (sdist or wheel) or interior contents.</li> <li>Metadata support: this PEP refers to “digital attestations” rather than just “digital signatures” to emphasize the ideal presence of additional metadata within the cryptographic envelope.<p>For example, to prevent domain separation between a distribution’s name and its contents, this PEP uses ‘<a class="reference external" href="https://github.com/in-toto/attestation/blob/v1.0/spec/v1.0/statement.md">Statements</a>’ from the <a class="reference external" href="https://in-toto.io/">in-toto project</a> to bind the distribution’s contents (via SHA-256 digest) to its filename.</p> </li> </ol> </section> <section id="previous-work"> <h3><a class="toc-backref" href="#previous-work" role="doc-backlink">Previous Work</a></h3> <section id="pgp-signatures"> <span id="id1"></span><h4><a class="toc-backref" href="#pgp-signatures" role="doc-backlink">PGP signatures</a></h4> <p>PyPI and other indices have historically supported PGP signatures on uploaded distributions. These could be supplied during upload, and could be retrieved by installing clients via the <code class="docutils literal notranslate"><span class="pre">data-gpg-sig</span></code> attribute in the <a class="pep reference internal" href="../pep-0503/" title="PEP 503 – Simple Repository API">PEP 503</a> API, the <code class="docutils literal notranslate"><span class="pre">gpg-sig</span></code> key on the <a class="pep reference internal" href="../pep-0691/" title="PEP 691 – JSON-based Simple API for Python Package Indexes">PEP 691</a> API, or via an adjacent <code class="docutils literal notranslate"><span class="pre">.asc</span></code>-suffixed URL.</p> <p>PGP signature uploads have been disabled on PyPI since <a class="reference external" href="https://blog.pypi.org/posts/2023-05-23-removing-pgp/">May 2023</a>, after <a class="reference external" href="https://blog.yossarian.net/2023/05/21/PGP-signatures-on-PyPI-worse-than-useless">an investigation</a> determined that the majority of signatures (which, themselves, constituted a tiny percentage of overall uploads) could not be associated with a public key or otherwise meaningfully verified.</p> <p>In their previously supported form on PyPI, PGP signatures satisfied considerations (1) and (3) above but not (2) (owing to the need for external keyservers and key distribution) or (4) (due to PGP signatures typically being constructed over just an input file, without any associated signed metadata).</p> </section> <section id="wheel-signatures"> <h4><a class="toc-backref" href="#wheel-signatures" role="doc-backlink">Wheel signatures</a></h4> <p><a class="pep reference internal" href="../pep-0427/" title="PEP 427 – The Wheel Binary Package Format 1.0">PEP 427</a> (and its <a class="reference external" href="https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format" title="(in Python Packaging User Guide)"><span class="xref std std-ref">living PyPA counterpart</span></a>) specify the <a class="reference external" href="https://packaging.python.org/en/latest/glossary/#term-Wheel" title="(in Python Packaging User Guide)"><span class="xref std std-term">wheel format</span></a>.</p> <p>This format includes accommodations for digital signatures embedded directly into the wheel, in either JWS or S/MIME format. These signatures are specified over a <a class="pep reference internal" href="../pep-0376/" title="PEP 376 – Database of Installed Python Distributions">PEP 376</a> RECORD, which is modified to include a cryptographic digest for each recorded file in the wheel.</p> <p>While wheel signatures are fully specified, they do not appear to be broadly used; the official <a class="reference external" href="https://github.com/pypa/wheel">wheel tooling</a> deprecated signature generation and verification support <a class="reference external" href="https://wheel.readthedocs.io/en/stable/news.html">in 0.32.0</a>, which was released in 2018.</p> <p>Additionally, wheel signatures do not satisfy any of the above considerations (due to the “attached” nature of the signatures, non-verifiability on the index itself, and support for wheels only).</p> </section> </section> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <section id="upload-endpoint-changes"> <span id="upload-endpoint"></span><h3><a class="toc-backref" href="#upload-endpoint-changes" role="doc-backlink">Upload endpoint changes</a></h3> <p>The current upload API is not standardized. However, we propose the following changes to it:</p> <ul class="simple"> <li>In addition to the current top-level <code class="docutils literal notranslate"><span class="pre">content</span></code> and <code class="docutils literal notranslate"><span class="pre">gpg_signature</span></code> fields, the index <strong>SHALL</strong> accept <code class="docutils literal notranslate"><span class="pre">attestations</span></code> as an additional multipart form field.</li> <li>The new <code class="docutils literal notranslate"><span class="pre">attestations</span></code> field <strong>SHALL</strong> be a JSON array.</li> <li>The <code class="docutils literal notranslate"><span class="pre">attestations</span></code> array <strong>SHALL</strong> have one or more items, each a JSON object representing an individual attestation.</li> <li>Each attestation object <strong>MUST</strong> be verifiable by the index. If the index fails to verify any attestation in <code class="docutils literal notranslate"><span class="pre">attestations</span></code>, it <strong>MUST</strong> reject the upload. The format of attestation objects is defined under <a class="reference internal" href="#attestation-object"><span class="std std-ref">Attestation objects</span></a> and the process for verifying attestations is defined under <a class="reference internal" href="#attestation-verification"><span class="std std-ref">Attestation verification</span></a>.</li> </ul> </section> <section id="index-changes"> <h3><a class="toc-backref" href="#index-changes" role="doc-backlink">Index changes</a></h3> <section id="simple-index"> <h4><a class="toc-backref" href="#simple-index" role="doc-backlink">Simple Index</a></h4> <p>The following changes are made to the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#simple-repository-api-base" title="(in Python Packaging User Guide)"><span class="xref std std-ref">simple repository API</span></a>:</p> <ul> <li>When an uploaded file has one or more attestations, the index <strong>MAY</strong> provide a provenance file containing attestations associated with a given distribution. The format of the provenance file <strong>SHALL</strong> be a JSON-encoded <a class="reference internal" href="#provenance-object"><span class="std std-ref">provenance object</span></a>, which <strong>SHALL</strong> contain the file’s attestations.<p>The location of the provenance file is signaled by the index via the <code class="docutils literal notranslate"><span class="pre">data-provenance</span></code> attribute.</p> </li> <li>When a provenance file is present, the index <strong>MAY</strong> include a <code class="docutils literal notranslate"><span class="pre">data-provenance</span></code> attribute on its file link. The value of the <code class="docutils literal notranslate"><span class="pre">data-provenance</span></code> attribute <strong>SHALL</strong> be a fully qualified URL, signaling the the file’s provenance can be found at that URL. This URL <strong>MUST</strong> represent a <a class="reference external" href="https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features/">secure origin</a>.<p>The following table provides examples of release file URLs, <code class="docutils literal notranslate"><span class="pre">data-provenance</span></code> values, and their resulting provenance file URLs.</p> <table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">File URL</th> <th class="head"><code class="docutils literal notranslate"><span class="pre">data-provenance</span></code></th> <th class="head">Provenance URL</th> </tr> </thead> <tbody> <tr class="row-even"><td><a class="reference external" href="https://example.com/sampleproject-1.2.3.tar.gz">https://example.com/sampleproject-1.2.3.tar.gz</a></td> <td><code class="docutils literal notranslate"><span class="pre">https://example.com/sampleproject-1.2.3.tar.gz.provenance</span></code></td> <td><a class="reference external" href="https://example.com/sampleproject-1.2.3.tar.gz.provenance">https://example.com/sampleproject-1.2.3.tar.gz.provenance</a></td> </tr> <tr class="row-odd"><td><a class="reference external" href="https://example.com/sampleproject-1.2.3.tar.gz">https://example.com/sampleproject-1.2.3.tar.gz</a></td> <td><code class="docutils literal notranslate"><span class="pre">https://other.example.com/sampleproject-1.2.3.tar.gz/provenance</span></code></td> <td><a class="reference external" href="https://other.example.com/sampleproject-1.2.3.tar.gz/provenance">https://other.example.com/sampleproject-1.2.3.tar.gz/provenance</a></td> </tr> <tr class="row-even"><td><a class="reference external" href="https://example.com/sampleproject-1.2.3.tar.gz">https://example.com/sampleproject-1.2.3.tar.gz</a></td> <td><code class="docutils literal notranslate"><span class="pre">../relative</span></code></td> <td><em>(invalid: not a fully qualified URL)</em></td> </tr> <tr class="row-odd"><td><a class="reference external" href="https://example.com/sampleproject-1.2.3.tar.gz">https://example.com/sampleproject-1.2.3.tar.gz</a></td> <td><code class="docutils literal notranslate"><span class="pre">http://unencrypted.example.com/provenance</span></code></td> <td><em>(invalid: not a secure origin)</em></td> </tr> </tbody> </table> </li> <li>The index <strong>MAY</strong> choose to modify the provenance file. For example, the index <strong>MAY</strong> permit adding additional attestations and verification materials, such as attestations from third-party auditors or other services.<p>See <a class="reference internal" href="#changes-to-provenance-objects"><span class="std std-ref">Changes to provenance objects</span></a> for an additional discussion of reasons why a file’s provenance may change.</p> </li> </ul> </section> <section id="json-based-simple-api"> <h4><a class="toc-backref" href="#json-based-simple-api" role="doc-backlink">JSON-based Simple API</a></h4> <p>The following changes are made to the <a class="reference external" href="https://packaging.python.org/en/latest/specifications/simple-repository-api/#simple-repository-api-json" title="(in Python Packaging User Guide)"><span class="xref std std-ref">JSON simple API</span></a>:</p> <ul> <li>When an uploaded file has one or more attestations, the index <strong>MAY</strong> include a <code class="docutils literal notranslate"><span class="pre">provenance</span></code> key in the <code class="docutils literal notranslate"><span class="pre">file</span></code> dictionary for that file.<p>The value of the <code class="docutils literal notranslate"><span class="pre">provenance</span></code> key <strong>SHALL</strong> be either a JSON string or <code class="docutils literal notranslate"><span class="pre">null</span></code>. If <code class="docutils literal notranslate"><span class="pre">provenance</span></code> is not <code class="docutils literal notranslate"><span class="pre">null</span></code>, it <strong>SHALL</strong> be a URL to the associated provenance file.</p> <p>See <a class="reference internal" href="#appendix-3"><span class="std std-ref">Appendix 3: Simple JSON API size considerations</span></a> for an explanation of the technical decision to embed the SHA-256 digest in the JSON API, rather than the full <a class="reference internal" href="#provenance-object"><span class="std std-ref">provenance object</span></a>.</p> </li> </ul> <p>These changes require a version change to the JSON API:</p> <ul class="simple"> <li>The <code class="docutils literal notranslate"><span class="pre">api-version</span></code> <strong>SHALL</strong> specify version 1.3 or later.</li> </ul> </section> </section> <section id="attestation-objects"> <span id="attestation-object"></span><h3><a class="toc-backref" href="#attestation-objects" role="doc-backlink">Attestation objects</a></h3> <p>An attestation object is a JSON object with several required keys; applications or signers may include additional keys so long as all explicitly listed keys are provided. The required layout of an attestation object is provided as pseudocode below.</p> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Attestation</span><span class="p">:</span> <span class="n">version</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The attestation object&#39;s version, which is always 1.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">verification_material</span><span class="p">:</span> <span class="n">VerificationMaterial</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Cryptographic materials used to verify `envelope`.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">envelope</span><span class="p">:</span> <span class="n">Envelope</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The enveloped attestation statement and signature.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Envelope</span><span class="p">:</span> <span class="n">statement</span><span class="p">:</span> <span class="nb">bytes</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The attestation statement.</span> <span class="sd"> This is represented as opaque bytes on the wire (encoded as base64),</span> <span class="sd"> but it MUST be an JSON in-toto v1 Statement.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">signature</span><span class="p">:</span> <span class="nb">bytes</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> A signature for the above statement, encoded as base64.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">VerificationMaterial</span><span class="p">:</span> <span class="n">certificate</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The signing certificate, as `base64(DER(cert))`.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">transparency_entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">object</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> One or more transparency log entries for this attestation&#39;s signature</span> <span class="sd"> and certificate.</span> <span class="sd"> &quot;&quot;&quot;</span> </pre></div> </div> <p>A full data model for each object in <code class="docutils literal notranslate"><span class="pre">transparency_entries</span></code> is provided in <a class="reference internal" href="#appendix-2"><span class="std std-ref">Appendix 2: Data models for Transparency Log Entries</span></a>. Attestation objects <strong>SHOULD</strong> include one or more transparency log entries, and <strong>MAY</strong> include additional keys for other sources of signed time (such as an <span class="target" id="index-0"></span><a class="rfc reference external" href="https://datatracker.ietf.org/doc/html/rfc3161.html"><strong>RFC 3161</strong></a> Time Stamping Authority or a <a class="reference external" href="https://blog.cloudflare.com/roughtime">Roughtime</a> server).</p> <p>Attestation objects are versioned; this PEP specifies version 1. Each version is tied to a single cryptographic suite to minimize unnecessary cryptographic agility. In version 1, the suite is as follows:</p> <ul class="simple"> <li>Certificates are specified as X.509 certificates, and comply with the profile in <span class="target" id="index-1"></span><a class="rfc reference external" href="https://datatracker.ietf.org/doc/html/rfc5280.html"><strong>RFC 5280</strong></a>.</li> <li>The message signature algorithm is ECDSA, with the P-256 curve for public keys and SHA-256 as the cryptographic digest function.</li> </ul> <p>Future PEPs may change this suite (and the overall shape of the attestation object) by selecting a new version number.</p> <section id="attestation-statement-and-signature-generation"> <span id="payload-and-signature-generation"></span><h4><a class="toc-backref" href="#attestation-statement-and-signature-generation" role="doc-backlink">Attestation statement and signature generation</a></h4> <p>The <em>attestation statement</em> is the actual claim that is cryptographically signed over within the attestation object (i.e., the <code class="docutils literal notranslate"><span class="pre">envelope.statement</span></code>).</p> <p>The attestation statement is encoded as a <a class="reference external" href="https://github.com/in-toto/attestation/blob/v1.0/spec/v1.0/statement.md">v1 in-toto Statement object</a>, in JSON form. When serialized the statement is treated as an opaque binary blob, avoiding the need for canonicalization. An example JSON-encoded statement is provided in <a class="reference internal" href="#appendix-4"><span class="std std-ref">Appendix 4: Example attestation statement</span></a>.</p> <p>In addition to being a v1 in-toto Statement, the attestation statement is constrained in the following ways:</p> <ul class="simple"> <li>The in-toto <code class="docutils literal notranslate"><span class="pre">subject</span></code> <strong>MUST</strong> contain only a single subject.</li> <li><code class="docutils literal notranslate"><span class="pre">subject[0].name</span></code> is the distribution’s filename, which <strong>MUST</strong> be a valid <a class="reference external" href="https://packaging.python.org/en/latest/specifications/source-distribution-format/#source-distribution-format" title="(in Python Packaging User Guide)"><span class="xref std std-ref">source distribution</span></a> or <a class="reference external" href="https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format" title="(in Python Packaging User Guide)"><span class="xref std std-ref">wheel distribution</span></a> filename.</li> <li><code class="docutils literal notranslate"><span class="pre">subject[0].digest</span></code> <strong>MUST</strong> contain a SHA-256 digest. Other digests <strong>MAY</strong> be present. The digests <strong>MUST</strong> be represented as hexadecimal strings.</li> <li>The following <code class="docutils literal notranslate"><span class="pre">predicateType</span></code> values are supported:<ul> <li><a class="reference external" href="https://slsa.dev/provenance/v1">SLSA Provenance</a>: <code class="docutils literal notranslate"><span class="pre">https://slsa.dev/provenance/v1</span></code></li> <li><a class="reference external" href="https://docs.pypi.org/attestations/publish/v1">PyPI Publish Attestation</a>: <code class="docutils literal notranslate"><span class="pre">https://docs.pypi.org/attestations/publish/v1</span></code></li> </ul> </li> </ul> <p>The signature over this statement is constructed using the <a class="reference external" href="https://github.com/secure-systems-lab/dsse/blob/v1.0.0/protocol.md">v1 DSSE signature protocol</a>, with a <code class="docutils literal notranslate"><span class="pre">PAYLOAD_TYPE</span></code> of <code class="docutils literal notranslate"><span class="pre">application/vnd.in-toto+json</span></code> and a <code class="docutils literal notranslate"><span class="pre">PAYLOAD_BODY</span></code> of the JSON-encoded statement above. No other <code class="docutils literal notranslate"><span class="pre">PAYLOAD_TYPE</span></code> is permitted.</p> </section> </section> <section id="provenance-objects"> <span id="provenance-object"></span><h3><a class="toc-backref" href="#provenance-objects" role="doc-backlink">Provenance objects</a></h3> <p>The index will serve uploaded attestations along with metadata that can assist in verifying them in the form of JSON serialized objects.</p> <p>These <em>provenance objects</em> will be available via both the Simple Index and JSON-based Simple API as described above, and will have the following layout:</p> <div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;attestation_bundles&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;publisher&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;kind&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;important-ci-service&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;claims&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span> <span class="w"> </span><span class="nt">&quot;vendor-property&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;foo&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;another-property&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">123</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="nt">&quot;attestations&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span> <span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* attestation 1 ... */</span><span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* attestation 2 ... */</span><span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">]</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">]</span> <span class="p">}</span> </pre></div> </div> <p>or, as pseudocode:</p> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Publisher</span><span class="p">:</span> <span class="n">kind</span><span class="p">:</span> <span class="n">string</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The kind of Trusted Publisher.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">claims</span><span class="p">:</span> <span class="nb">object</span> <span class="o">|</span> <span class="kc">None</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Any context-specific claims retained by the index during Trusted Publisher</span> <span class="sd"> authentication.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">_rest</span><span class="p">:</span> <span class="nb">object</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Each publisher object is open-ended, meaning that it MAY contain additional</span> <span class="sd"> fields beyond the ones specified explicitly above. This field signals that,</span> <span class="sd"> but is not itself present.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">AttestationBundle</span><span class="p">:</span> <span class="n">publisher</span><span class="p">:</span> <span class="n">Publisher</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The publisher associated with this set of attestations.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">attestations</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Attestation</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The set of attestations included in this bundle.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Provenance</span><span class="p">:</span> <span class="n">version</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The provenance object&#39;s version, which is always 1.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">attestation_bundles</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">AttestationBundle</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> One or more attestation &quot;bundles&quot;.</span> <span class="sd"> &quot;&quot;&quot;</span> </pre></div> </div> <ul> <li><code class="docutils literal notranslate"><span class="pre">version</span></code> is <code class="docutils literal notranslate"><span class="pre">1</span></code>. Like attestation objects, provenance objects are versioned, and this PEP only defines version <code class="docutils literal notranslate"><span class="pre">1</span></code>.</li> <li><code class="docutils literal notranslate"><span class="pre">attestation_bundles</span></code> is a <strong>required</strong> JSON array, containing one or more “bundles” of attestations. Each bundle corresponds to a signing identity (such as a Trusted Publishing identity), and contains one or more attestation objects.<p>As noted in the <code class="docutils literal notranslate"><span class="pre">Publisher</span></code> model, each <code class="docutils literal notranslate"><span class="pre">AttestationBundle.publisher</span></code> object is specific to its Trusted Publisher but must include at minimum:</p> <ul class="simple"> <li>A <code class="docutils literal notranslate"><span class="pre">kind</span></code> key, which <strong>MUST</strong> be a JSON string that uniquely identifies the kind of Trusted Publisher.</li> <li>A <code class="docutils literal notranslate"><span class="pre">claims</span></code> key, which <strong>MUST</strong> be a JSON object containing any context-specific claims retained by the index during Trusted Publisher authentication.</li> </ul> <p>All other keys in the publisher object are publisher-specific. A full illustrative example of a publisher object is provided in <a class="reference internal" href="#appendix-1"><span class="std std-ref">Appendix 1: Example Trusted Publisher Representation</span></a>.</p> <p>Each array of attestation objects is a superset of the <code class="docutils literal notranslate"><span class="pre">attestations</span></code> array supplied by the uploaded through the <code class="docutils literal notranslate"><span class="pre">attestations</span></code> field at upload time, as described in <a class="reference internal" href="#upload-endpoint"><span class="std std-ref">Upload endpoint changes</span></a> and <a class="reference internal" href="#changes-to-provenance-objects"><span class="std std-ref">Changes to provenance objects</span></a>.</p> </li> </ul> <section id="changes-to-provenance-objects"> <span id="id2"></span><h4><a class="toc-backref" href="#changes-to-provenance-objects" role="doc-backlink">Changes to provenance objects</a></h4> <p>Provenance objects are <em>not</em> immutable, and may change over time. Reasons for changes to the provenance object include but are not limited to:</p> <ul class="simple"> <li>Addition of new attestations for a pre-existing signing identity: the index <strong>MAY</strong> choose to allow additional attestations by pre-existing signing identities, such as newer attestation versions for already uploaded files.</li> <li>Addition of new signing identities and associated attestations: the index <strong>MAY</strong> choose to support attestations from sources other than the file’s uploader, such as third-party auditors or the index itself. These attestations may be performed asynchronously, requiring the index to insert them into the provenance object <em>post facto</em>.</li> </ul> </section> </section> <section id="attestation-verification"> <span id="id3"></span><h3><a class="toc-backref" href="#attestation-verification" role="doc-backlink">Attestation verification</a></h3> <p>Verifying an attestation object against a distribution file requires verification of each of the following:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">version</span></code> is <code class="docutils literal notranslate"><span class="pre">1</span></code>. The verifier <strong>MUST</strong> reject any other version.</li> <li><code class="docutils literal notranslate"><span class="pre">verification_material.certificate</span></code> is a valid signing certificate, as issued by an <em>a priori</em> trusted authority (such as a root of trust already present within the verifying client).</li> <li><code class="docutils literal notranslate"><span class="pre">verification_material.certificate</span></code> identifies an appropriate signing subject, such as the machine identity of the Trusted Publisher that published the package.</li> <li><code class="docutils literal notranslate"><span class="pre">envelope.statement</span></code> is a valid in-toto v1 Statement, with a subject and digest that <strong>MUST</strong> match the distribution’s filename and contents. For the distribution’s filename, matching <strong>MUST</strong> be performed by parsing using the appropriate source distribution or wheel filename format, as the statement’s subject may be equivalent but normalized.</li> <li><code class="docutils literal notranslate"><span class="pre">envelope.signature</span></code> is a valid signature for <code class="docutils literal notranslate"><span class="pre">envelope.statement</span></code> corresponding to <code class="docutils literal notranslate"><span class="pre">verification_material.certificate</span></code>, as reconstituted via the <a class="reference external" href="https://github.com/secure-systems-lab/dsse/blob/v1.0.0/protocol.md">v1 DSSE signature protocol</a>.</li> </ul> <p>In addition to the above required steps, a verifier <strong>MAY</strong> additionally verify <code class="docutils literal notranslate"><span class="pre">verification_material.transparency_entries</span></code> on a policy basis, e.g. requiring at least one transparency log entry or a threshold of entries. When verifying transparency entries, the verifier <strong>MUST</strong> confirm that the inclusion time for each entry lies within the signing certificate’s validity period.</p> </section> </section> <section id="security-implications"> <h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2> <p>This PEP is primarily “mechanical” in nature; it provides layouts for structuring and serving verifiable digital attestations without specifying higher level security “policies” around attestation validity, thresholds between attestations, and so forth.</p> <section id="cryptographic-agility-in-attestations"> <h3><a class="toc-backref" href="#cryptographic-agility-in-attestations" role="doc-backlink">Cryptographic agility in attestations</a></h3> <p>Algorithmic agility is a common source of exploitable vulnerabilities in cryptographic schemes. This PEP limits algorithmic agility in two ways:</p> <ul class="simple"> <li>All algorithms are specified in a single suite, rather than a geometric collection of parameters. This makes it impossible (for example) for an attacker to select a strong signature algorithm with a weak hash function, compromising the scheme as a whole.</li> <li>Attestation objects are versioned, and may only contain the algorithmic suite specified for their version. If a specific suite is considered insecure in the future, clients may choose to blanket reject or qualify verifications of attestations that contain that suite.</li> </ul> </section> <section id="index-trust"> <h3><a class="toc-backref" href="#index-trust" role="doc-backlink">Index trust</a></h3> <p>This PEP does <strong>not</strong> increase (or decrease) trust in the index itself: the index is still effectively trusted to honestly deliver unmodified package distributions, since a dishonest index capable of modifying package contents could also dishonestly modify or omit package attestations. As a result, this PEP’s presumption of index trust is equivalent to the unstated presumption with earlier mechanisms, like PGP and wheel signatures.</p> <p>This PEP does not preclude or exclude future index trust mechanisms, such as <a class="pep reference internal" href="../pep-0458/" title="PEP 458 – Secure PyPI downloads with signed repository metadata">PEP 458</a> and/or <a class="pep reference internal" href="../pep-0480/" title="PEP 480 – Surviving a Compromise of PyPI: End-to-end signing of packages">PEP 480</a>.</p> </section> </section> <section id="recommendations"> <h2><a class="toc-backref" href="#recommendations" role="doc-backlink">Recommendations</a></h2> <p>This PEP recommends, but does not mandate, that attestation objects contain one or more verifiable sources of signed time that corroborate the signing certificate’s claimed validity period. Indices that implement this PEP may choose to strictly enforce this requirement.</p> </section> <section id="appendix-1-example-trusted-publisher-representation"> <span id="appendix-1"></span><h2><a class="toc-backref" href="#appendix-1-example-trusted-publisher-representation" role="doc-backlink">Appendix 1: Example Trusted Publisher Representation</a></h2> <p>This appendix provides a fictional example of a <code class="docutils literal notranslate"><span class="pre">publisher</span></code> key within a simple JSON API <code class="docutils literal notranslate"><span class="pre">project.files[].provenance</span></code> listing:</p> <div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="nt">&quot;publisher&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;kind&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;GitHub&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;claims&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;ref&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;refs/tags/v1.0.0&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;sha&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;da39a3ee5e6b4b0d3255bfef95601890afd80709&quot;</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="nt">&quot;repository_name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;HolyGrail&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;repository_owner&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;octocat&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;repository_owner_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;1&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;workflow_filename&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;publish.yml&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;environment&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span> <span class="p">}</span> </pre></div> </div> </section> <section id="appendix-2-data-models-for-transparency-log-entries"> <span id="appendix-2"></span><h2><a class="toc-backref" href="#appendix-2-data-models-for-transparency-log-entries" role="doc-backlink">Appendix 2: Data models for Transparency Log Entries</a></h2> <p>This appendix contains pseudocoded data models for transparency log entries in attestation objects. Each transparency log entry serves as a source of signed inclusion time, and can be verified either online or offline.</p> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">TransparencyLogEntry</span><span class="p">:</span> <span class="n">log_index</span><span class="p">:</span> <span class="nb">int</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The global index of the log entry, used when querying the log.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">log_id</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> An opaque, unique identifier for the log.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">entry_kind</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The kind (type) of log entry.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">entry_version</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The version of the log entry&#39;s submitted format.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">integrated_time</span><span class="p">:</span> <span class="nb">int</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The UNIX timestamp from the log from when the entry was persisted.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">inclusion_proof</span><span class="p">:</span> <span class="n">InclusionProof</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The actual inclusion proof of the log entry.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">InclusionProof</span><span class="p">:</span> <span class="n">log_index</span><span class="p">:</span> <span class="nb">int</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The index of the entry in the tree it was written to.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">root_hash</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The digest stored at the root of the Merkle tree at the time of proof</span> <span class="sd"> generation.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">tree_size</span><span class="p">:</span> <span class="nb">int</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The size of the Merkle tree at the time of proof generation.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">hashes</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> A list of hashes required to complete the inclusion proof, sorted</span> <span class="sd"> in order from leaf to root. The leaf and root hashes are not themselves</span> <span class="sd"> included in this list; the root is supplied via `root_hash` and the client</span> <span class="sd"> must calculate the leaf hash.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">checkpoint</span><span class="p">:</span> <span class="nb">str</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> The signed tree head&#39;s signature, at the time of proof generation.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">cosigned_checkpoints</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Cosigned checkpoints from zero or more log witnesses.</span> <span class="sd"> &quot;&quot;&quot;</span> </pre></div> </div> </section> <section id="appendix-3-simple-json-api-size-considerations"> <span id="appendix-3"></span><h2><a class="toc-backref" href="#appendix-3-simple-json-api-size-considerations" role="doc-backlink">Appendix 3: Simple JSON API size considerations</a></h2> <p>A previous draft of this PEP required embedding each <a class="reference internal" href="#provenance-object"><span class="std std-ref">provenance object</span></a> directly into its appropriate part of the JSON Simple API.</p> <p>The current version of this PEP embeds the SHA-256 digest of the provenance object instead. This is done for size and network bandwidth consideration reasons:</p> <ol class="arabic simple"> <li>We estimate the typical size of an attestation object to be approximately 5.3 KB of JSON.</li> <li>We conservatively estimate that indices eventually host around 3 attestations per release file, or approximately 15.9 KB of JSON per combined provenance object.</li> <li>As of May 2024, the average project on PyPI has approximately 21 release files. We conservatively expect this average to increase over time.</li> <li>Combined, these numbers imply that a typical project might expect to host between 60 and 70 attestations, or approximately 339 KB of additional JSON in its “project detail” endpoint.</li> </ol> <p>These numbers are significantly worse in “pathological” cases, where projects have hundreds or thousands of releases and/or dozens of files per release.</p> </section> <section id="appendix-4-example-attestation-statement"> <span id="appendix-4"></span><h2><a class="toc-backref" href="#appendix-4-example-attestation-statement" role="doc-backlink">Appendix 4: Example attestation statement</a></h2> <p>Given a source distribution <code class="docutils literal notranslate"><span class="pre">sampleproject-1.2.3.tar.gz</span></code> with a SHA-256 digest of <code class="docutils literal notranslate"><span class="pre">e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855</span></code>, the following is an appropriate in-toto Statement, as a JSON object:</p> <div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;https://in-toto.io/Statement/v1&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;subject&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sampleproject-1.2.3.tar.gz&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;digest&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">&quot;sha256&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855&quot;</span><span class="p">}</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">],</span> <span class="w"> </span><span class="nt">&quot;predicateType&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;https://some-arbitrary-predicate.example.com/v1&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;predicate&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;something-else&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;foo&quot;</span> <span class="w"> </span><span class="p">}</span> <span class="p">}</span> </pre></div> </div> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0740.rst">https://github.com/python/peps/blob/main/peps/pep-0740.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0740.rst">2024-09-27 00:47:01 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale-and-motivation">Rationale and Motivation</a><ul> <li><a class="reference internal" href="#design-considerations">Design Considerations</a></li> <li><a class="reference internal" href="#previous-work">Previous Work</a><ul> <li><a class="reference internal" href="#pgp-signatures">PGP signatures</a></li> <li><a class="reference internal" href="#wheel-signatures">Wheel signatures</a></li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#upload-endpoint-changes">Upload endpoint changes</a></li> <li><a class="reference internal" href="#index-changes">Index changes</a><ul> <li><a class="reference internal" href="#simple-index">Simple Index</a></li> <li><a class="reference internal" href="#json-based-simple-api">JSON-based Simple API</a></li> </ul> </li> <li><a class="reference internal" href="#attestation-objects">Attestation objects</a><ul> <li><a class="reference internal" href="#attestation-statement-and-signature-generation">Attestation statement and signature generation</a></li> </ul> </li> <li><a class="reference internal" href="#provenance-objects">Provenance objects</a><ul> <li><a class="reference internal" href="#changes-to-provenance-objects">Changes to provenance objects</a></li> </ul> </li> <li><a class="reference internal" href="#attestation-verification">Attestation verification</a></li> </ul> </li> <li><a class="reference internal" href="#security-implications">Security Implications</a><ul> <li><a class="reference internal" href="#cryptographic-agility-in-attestations">Cryptographic agility in attestations</a></li> <li><a class="reference internal" href="#index-trust">Index trust</a></li> </ul> </li> <li><a class="reference internal" href="#recommendations">Recommendations</a></li> <li><a class="reference internal" href="#appendix-1-example-trusted-publisher-representation">Appendix 1: Example Trusted Publisher Representation</a></li> <li><a class="reference internal" href="#appendix-2-data-models-for-transparency-log-entries">Appendix 2: Data models for Transparency Log Entries</a></li> <li><a class="reference internal" href="#appendix-3-simple-json-api-size-considerations">Appendix 3: Simple JSON API size considerations</a></li> <li><a class="reference internal" href="#appendix-4-example-attestation-statement">Appendix 4: Example attestation statement</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0740.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10