索引托管证明¶
注意
本规范最初定义于PEP 740。
规范¶
上传端点更改¶
重要提示
“传统”上传 API 未标准化。有关如何上传证明,请参阅PyPI 的上传 API 文档。
证明对象¶
证明对象是一个 JSON 对象,包含几个必需的键;应用程序或签名者可以包含附加键,只要所有明确列出的键都已提供。证明对象的必需布局以下面的伪代码形式提供。
@dataclass
class Attestation:
version: Literal[1]
"""
The attestation object's version, which is always 1.
"""
verification_material: VerificationMaterial
"""
Cryptographic materials used to verify `envelope`.
"""
envelope: Envelope
"""
The enveloped attestation statement and signature.
"""
@dataclass
class Envelope:
statement: bytes
"""
The attestation statement.
This is represented as opaque bytes on the wire (encoded as base64),
but it MUST be an JSON in-toto v1 Statement.
"""
signature: bytes
"""
A signature for the above statement, encoded as base64.
"""
@dataclass
class VerificationMaterial:
certificate: str
"""
The signing certificate, as `base64(DER(cert))`.
"""
transparency_entries: list[object]
"""
One or more transparency log entries for this attestation's signature
and certificate.
"""
transparency_entries
中每个对象的完整数据模型在附录:透明度日志条目的数据模型中提供。证明对象应该包含一个或多个透明度日志条目,并且可以包含用于其他签名时间来源的附加键(例如RFC 3161时间戳机构或Roughtime服务器)。
证明对象是版本化的;本 PEP 指定版本 1。每个版本都绑定到一个密码套件,以最大限度地减少不必要的密码敏捷性。在版本 1 中,套件如下:
证书被指定为 X.509 证书,并符合RFC 5280中的配置文件。
消息签名算法是 ECDSA,公钥使用 P-256 曲线,SHA-256 作为加密摘要函数。
未来的 PEP 可以通过选择新的版本号来更改此套件(以及证明对象的整体形状)。
证明声明和签名生成¶
证明声明是证明对象中进行密码签名的实际声明(即envelope.statement
)。
证明声明以 JSON 形式编码为v1 in-toto Statement 对象。序列化后,该声明被视为不透明的二进制数据块,从而避免了规范化的需要。
除了是 v1 in-toto Statement 之外,证明声明还受到以下限制:
in-toto
subject
必须只包含一个主题。subject[0].digest
必须包含 SHA-256 摘要。其他摘要可以存在。摘要必须表示为十六进制字符串。支持以下
predicateType
值:
此声明的签名使用v1 DSSE 签名协议构建,PAYLOAD_TYPE
为application/vnd.in-toto+json
,PAYLOAD_BODY
为上述 JSON 编码的声明。不允许使用其他PAYLOAD_TYPE
。
来源对象¶
索引将提供上传的证明以及可以协助验证它们的元数据,形式为 JSON 序列化对象。
这些来源对象将通过上述简单索引和基于 JSON 的简单 API 提供,并具有以下布局:
{
"version": 1,
"attestation_bundles": [
{
"publisher": {
"kind": "important-ci-service",
"claims": {},
"vendor-property": "foo",
"another-property": 123
},
"attestations": [
{ /* attestation 1 ... */ },
{ /* attestation 2 ... */ }
]
}
]
}
或者,以伪代码形式:
@dataclass
class Publisher:
kind: string
"""
The kind of Trusted Publisher.
"""
claims: object | None
"""
Any context-specific claims retained by the index during Trusted Publisher
authentication.
"""
_rest: object
"""
Each publisher object is open-ended, meaning that it MAY contain additional
fields beyond the ones specified explicitly above. This field signals that,
but is not itself present.
"""
@dataclass
class AttestationBundle:
publisher: Publisher
"""
The publisher associated with this set of attestations.
"""
attestations: list[Attestation]
"""
The set of attestations included in this bundle.
"""
@dataclass
class Provenance:
version: Literal[1]
"""
The provenance object's version, which is always 1.
"""
attestation_bundles: list[AttestationBundle]
"""
One or more attestation "bundles".
"""
version
为1
。与证明对象一样,来源对象是版本化的,本 PEP 仅定义版本1
。attestation_bundles
是一个必需的 JSON 数组,包含一个或多个证明“包”。每个包对应一个签名身份(例如受信任的发布身份),并包含一个或多个证明对象。如
Publisher
模型中所述,每个AttestationBundle.publisher
对象都是特定于其受信任的发布者的,但必须至少包含:一个
kind
键,它必须是一个唯一标识受信任的发布者类型的 JSON 字符串。一个
claims
键,它必须是一个 JSON 对象,包含索引在受信任的发布者身份验证期间保留的任何特定于上下文的声明。
发布者对象中的所有其他键都是发布者特定的。
每个证明对象数组是上传时通过
attestations
字段提供的attestations
数组的超集,如上传端点更改和来源对象的更改所述。
来源对象的更改¶
来源对象不是不可变的,并且可能随时间变化。来源对象更改的原因包括但不限于:
为现有签名身份添加新证明:索引可以选择允许现有签名身份提供额外证明,例如已上传文件的更新证明版本。
添加新的签名身份和相关证明:索引可以选择支持来自文件上传者以外的来源的证明,例如第三方审计员或索引本身。这些证明可以异步执行,要求索引在事后将其插入来源对象。
证明验证¶
根据分发文件验证证明对象需要验证以下各项:
version
是1
。验证器必须拒绝任何其他版本。verification_material.certificate
是一个有效的签名证书,由先验受信任的机构(例如验证客户端中已存在的信任根)颁发。verification_material.certificate
标识适当的签名主题,例如发布包的受信任发布者的机器身份。envelope.statement
是一个有效的 in-toto v1 Statement,其主题和摘要必须与分发的文件名和内容匹配。对于分发的文件名,匹配必须通过使用适当的源分发或 wheel 文件名格式进行解析来执行,因为声明的主题可能等效但已规范化。envelope.signature
是envelope.statement
的有效签名,对应于verification_material.certificate
,通过v1 DSSE 签名协议重新构成。
除了上述必需步骤外,验证器可以根据策略额外验证verification_material.transparency_entries
,例如要求至少一个透明度日志条目或达到阈值数量的条目。在验证透明度条目时,验证器必须确认每个条目的包含时间在签名证书的有效期内。
附录:透明度日志条目的数据模型¶
本附录包含证明对象中透明度日志条目的伪代码数据模型。每个透明度日志条目都作为签名包含时间的来源,并且可以在线或离线验证。
@dataclass
class TransparencyLogEntry:
log_index: int
"""
The global index of the log entry, used when querying the log.
"""
log_id: str
"""
An opaque, unique identifier for the log.
"""
entry_kind: str
"""
The kind (type) of log entry.
"""
entry_version: str
"""
The version of the log entry's submitted format.
"""
integrated_time: int
"""
The UNIX timestamp from the log from when the entry was persisted.
"""
inclusion_proof: InclusionProof
"""
The actual inclusion proof of the log entry.
"""
@dataclass
class InclusionProof:
log_index: int
"""
The index of the entry in the tree it was written to.
"""
root_hash: str
"""
The digest stored at the root of the Merkle tree at the time of proof
generation.
"""
tree_size: int
"""
The size of the Merkle tree at the time of proof generation.
"""
hashes: list[str]
"""
A list of hashes required to complete the inclusion proof, sorted
in order from leaf to root. The leaf and root hashes are not themselves
included in this list; the root is supplied via `root_hash` and the client
must calculate the leaf hash.
"""
checkpoint: str
"""
The signed tree head's signature, at the time of proof generation.
"""
cosigned_checkpoints: list[str]
"""
Cosigned checkpoints from zero or more log witnesses.
"""