owned this note
owned this note
Published
Linked with GitHub
# Securing Artifact Policy with TUF and in-toto
## Implimentation Proposal for Witness and Archivista
The software supply chain faces numerous attack risks, making it crucial to establish a secure and trustworthy system for managing software metadata. A key component is the authoritative policy for a software repository, specifying rules based on attestation metadata.
Distributing and trusting the policy poses challenges. It must maintain integrity and confidentiality and be trusted by all parties in the supply chain. The proposed solution uses TestifySec Witness policies and integrates Archivista as the API for the TUF repository. Witness policies enable trustworthiness of software artifacts, while Archivista manages in-toto attestations.
The system includes four components: Target Client, Target Service, Snapshot Service, and Timestamp Service, with Archivista handling attestations. Archivista stores attestations in an object store, with some data in a queryable metadata store, accessible through a GraphQL API. This allows for querying relationships between attestations.
By employing a TUF repository, Witness policies, and Archivista, this solution offers robust security and a reliable system for managing software metadata, ensuring secure distribution and trust in the software supply chain.
## Target Client Create Policy Overview
The Target Client is a client-side tool responsible for uploading target files to the repository. It is designed for human interaction, allowing users to securely upload target files which will then be processed by the Target Service to create and sign the corresponding target metadata.
### Workflow
1. **Sign Target File:** The user signs the target file using their private key stored on a CAC Card, HSM, or OIDC.
2. **Upload Signed Target File:** The user uploads the signed target file to Archivista (or the appropriate storage service).
```mermaid
stateDiagram
[*] --> TargetClient: Initialize
state TargetClient {
state SignTargetFile {
[*] --> SignWithClientPrivateKey: Start
SignWithClientPrivateKey --> UploadSignedTargetFile: Sign target file using private key from CAC Card, HSM, or OIDC
}
state UploadSignedTargetFile {
[*] --> UploadToArchivista: Start
UploadToArchivista --> [*] : Upload signed target file to Archivista
}
}
```
## Witness Verification Process
We aim to create a
The aim is to execute the command `witness verify filename.ext` with optional flags:
- --archivista-server: URL of the Archivista server for querying
- --public-key: Public key for verifying the TUF root
- --tenant-id: User's tenant ID
- --repo-url: Specific repository for verification
Also, we envision the witness library being used in admission controllers, network controllers (SPIRE), and system agents that continuosly verify that running software meets policy, even as it changes.
Steps involved:
1. Witness communicates with Archivista API.
2. Archivista locates the artifact's repository, identifying the first trusted attestation containing the file hash.
3. Witness downloads the TUF root and verifies its signature with the public key.
4. Witness queries TUF metadata for the repo's latest target.
5. Witness downloads the policy from Archivista.
6. Witness verifies the policy signature against TUF metadata.
7. Witness downloads artifact attestations from Archivista.
8. Witness verifies the signed attestation against the validated policy.
9. Witness (when running as an agent) will request a new policy according to the freshness policy.
```mermaid
stateDiagram
state "Communicate with Archivista API" as S1
state "Locate artifact's repository" as S2
state "Download and verify TUF root" as S3
state "Query TUF metadata" as S4
state "Download policy from Archivista" as S5
state "Verify policy signature" as S6
state "Download artifact attestations" as S7
state "Verify signed attestation" as S8
state "Request new policy (as agent)" as S9
S1 --> S2
S2 --> S3
S3 --> S4
S4 --> S5
S5 --> S6
S6 --> S7
S7 --> S8
S8 --> S9 : Freshness Policy
S9 --> S4
```
## Target Service Overview:
The Target Service is a microservice responsible for implementing the Targets role for delegated targets in a TUF repository. Its primary goal is to securely create and maintain the target metadata files for delegated targets upon receiving a new target file upload notification from Archivista.
The Target Service workflow consists of the following steps:
1. **Wait for target upload notification:** The service listens for notifications from Archivista about new target file uploads.
2. **Retrieve signed target file:** Upon receiving a notification, the service retrieves the signed target file from Archivista.
3. **Retrieve Targets metadata:** The service retrieves the Targets metadata file containing the certificate constraints for the delegated target, indicated by the repo path.
4. **Verify target file signature:** The service verifies the signature of the target file using the public key provided by the client and the constraints in the Targets metadata file. The target file is signed by the client using their private key stored on a CAC Card, HSM, or OIDC.
5. **Create target metadata:** If the signature is valid, the service creates target metadata for the delegated target, indicated by the repo path.
6. **Upload target metadata:** The service uploads the target metadata to Archivista, making it available for clients to access and download.
```mermaid
stateDiagram
[*] --> TargetService: Initialize
state TargetService {
state WaitForTargetUploadNotification {
[*] --> ListenForNotification: Start
ListenForNotification --> RetrieveSignedTargetFile: Receive notification from Archivista about new target file upload
}
state RetrieveSignedTargetFile {
[*] --> GetSignedTargetFile: Start
GetSignedTargetFile --> RetrieveTargetsMetadata: Retrieve signed target file from Archivista
}
state RetrieveTargetsMetadata {
[*] --> GetTargetsMetadata: Start
GetTargetsMetadata --> VerifyTargetFileSignature: Retrieve Targets metadata file containing certificate constraints
}
state VerifyTargetFileSignature {
[*] --> CheckSignature: Start
CheckSignature --> CreateTargetMetadata: Verify the signature of the target file using the constraints in the Targets metadata file
}
state CreateTargetMetadata {
[*] --> GenerateTargetMetadata: Start
GenerateTargetMetadata --> UploadTargetMetadata: Create target metadata for the delegated target (repo path)
}
state UploadTargetMetadata {
[*] --> UploadToArchivista: Start
UploadToArchivista --> [*]: Upload target metadata to Archivista
}
}
```
## Snapshot Service Overview:
The Snapshot service is a microservice responsible for implementing the TUF Snapshot role. Its primary goal is to securely create, sign, and maintain the Snapshot metadata file, ensuring that clients can access a consistent view of all files on the repository.
The Snapshot service workflow consists of the following steps:
1. **Retrieve all metadata:** The service retrieves all TUF metadata files (except timestamp) from Archivista for the tennant. Archivista should push this to the snapshot service.
2. **Create Snapshot metadata:** The service generates a new Snapshot metadata file using the retrieved metadata files, listing version numbers of all metadata files other than timestamp.json.
3. **Authenticate with Spire:** The service authenticates with Spire to obtain the SPIFFE SVID (Secure Production Identity Framework For Everyone - Secure Vector Identifier).
4. **Sign Snapshot metadata:** The service signs the Snapshot metadata file using the SPIFFE private key.
5. **Counter-sign with TSA:** The service requests a counter-signature from the Time-Stamp Authority (TSA), further enhancing the security and trustworthiness of the Snapshot metadata file.
6. **Upload signed Snapshot metadata:** The service uploads the signed and counter-signed Snapshot metadata file to Archivista (or the metadata storage service), ensuring that clients can access and download the latest Snapshot metadata.
By implementing the TUF Snapshot role, the Snapshot service provides a secure and efficient way for clients to access a consistent view of all files on the repository, enhancing the overall security and integrity of the system.
```mermaid
stateDiagram
[*] --> SnapshotService: Initialize
state SnapshotService {
state RetrieveAllMetadata {
[*] --> GetAllMetadata: Start
GetAllMetadata --> GetAllMetadataFromArchivista: Retrieve all metadata files (except timestamp.json) from Archivista for repo
GetAllMetadataFromArchivista --> CreateSnapshotMetadata: Return metadata files
}
state CreateSnapshotMetadata {
[*] --> GenerateSnapshot: Start
GenerateSnapshot --> AuthenticateWithSpire: Create Snapshot metadata using retrieved metadata files
}
state AuthenticateWithSpire {
[*] --> GetSpireSVID: Start
GetSpireSVID --> SignSnapshotMetadata: Retrieve SPIFFE SVID
}
state SignSnapshotMetadata {
[*] --> SignWithSVID: Start
SignWithSVID --> CounterSignWithTSA: Sign Snapshot metadata with SPIFFE private key
}
state CounterSignWithTSA {
[*] --> RequestTimestampSignature: Start
RequestTimestampSignature --> UploadSignedSnapshot: Get a counter-signature from the Time-Stamp Authority (TSA)
}
state UploadSignedSnapshot {
[*] --> UploadToArchivista: Start
UploadToArchivista --> [*] : Upload signed and counter-signed Snapshot metadata to Archivista
}
}
```
## Timestamp Service Overview
The Timestamp service is a microservice responsible for implementing the TUF Timestamp role. Its main goal is to securely create, sign, and maintain the Timestamp metadata file, allowing clients to quickly check if they have the most recent snapshot metadata available.
The Timestamp service workflow consists of the following steps:
1. **Retrieve snapshot metadata:** The service retrieves the current snapshot.json metadata file from the Archivista (or metadata storage service).
2. **Create Timestamp metadata:** The service generates a new Timestamp metadata file using the retrieved snapshot metadata. The Timestamp metadata file contains the hash and version number of the latest snapshot metadata file.
3. **Authenticate with Spire:** The service authenticates with Spire to obtain the SPIFFE SVID (Secure Production Identity Framework For Everyone - Secure Vector Identifier).
4. **Sign Timestamp metadata:** The service signs the Timestamp metadata file using the SPIFFE private key.
5. **Counter-sign with TSA:** The service requests a counter-signature from the Time-Stamp Authority (TSA), enhancing the security and trustworthiness of the Timestamp metadata file.
6. **Upload signed Timestamp metadata:** The service uploads the signed and counter-signed Timestamp metadata file to Archivista (or the metadata storage service), ensuring that clients can access and download the latest Timestamp metadata.
By implementing the TUF Timestamp role, the Timestamp service provides a secure and efficient way for clients to verify the freshness of the snapshot metadata.
```mermaid
stateDiagram
[*] --> TimestampService: Initialize
state TimestampService {
state RetrieveSnapshotMetadata {
[*] --> GetSnapshotMetadata: Start
GetSnapshotMetadata --> GetSnapshotMetadataFromArchivista: Retrieve snapshot.json metadata from Archivista
GetSnapshotMetadataFromArchivista --> CreateTimestampMetadata: Return snapshot metadata
}
state CreateTimestampMetadata {
[*] --> GenerateTimestamp: Start
GenerateTimestamp --> AuthenticateWithSpire: Create Timestamp metadata using retrieved snapshot metadata
}
state AuthenticateWithSpire {
[*] --> GetSpireSVID: Start
GetSpireSVID --> SignTimestampMetadata: Retrieve SPIFFE SVID
}
state SignTimestampMetadata {
[*] --> SignWithSVID: Start
SignWithSVID --> CounterSignWithTSA: Sign Timestamp metadata with SPIFFE private key
}
state CounterSignWithTSA {
[*] --> RequestTimestampSignature: Start
RequestTimestampSignature --> UploadSignedTimestamp: Get a counter-signature from the Time-Stamp Authority (TSA)
}
state UploadSignedTimestamp {
[*] --> UploadToArchivista: Start
UploadToArchivista --> [*] : Upload signed and counter-signed Timestamp metadata to Archivista
}
}
```
## TUF Repo Schema with Entgo
The TUF Repo Schema, implemented with Entgo, represents the data model for a TUF repository while maintaining the core concepts of TUF. The schema includes various entities for each TUF role, metadata files, and associated information such as certificate constraints, delegations, and support for an enhanced Dead Simple Signing Envelope (DSSE).
### Main Components
1. **Tennant**: Represents a tenant in the system with a unique identifier, allowing for multi-tenancy support.
2. **RootRole, TargetsRole, SnapshotRole, TimestampRole**: Entities representing the four main TUF roles, each with their respective thresholds and associated data. These roles are crucial in maintaining the TUF's security guarantees.
3. **CertificateConstraint**: Represents constraints for certificate verification during the process of signing and verifying metadata files. It includes information such as TSA certificate, CA certificate, public key, issuer URI, CA email, subject, max TTL, SAN DNS, SPIFFE ID, and CRL URL.
4. **SnapshotMetadata, TimestampMetadata, TargetsMetadata**: Entities representing the metadata files associated with each TUF role, including version and expiration information. These metadata files are crucial for maintaining the consistency and freshness of the repository.
5. **Delegation**: Represents a delegation of responsibility to a specific repo path, allowing for a hierarchical organization of targets and facilitating fine-grained access control.
6. **DSSE**: Represents an enhanced Dead Simple Signing Envelope with support for certificate chains and Time-Stamp Authorities (TSAs). It has a GitOID. This extended version of DSSE enhances the security and trustworthiness of the TUF metadata by providing strong authentication and non-repudiation.
### Entity Relationships
- Each Tennant has one RootRole.
- RootRole has one CertificateConstraint and has one each of TargetsRole, SnapshotRole, and TimestampRole.
- TargetsRole, SnapshotRole, and TimestampRole each have one CertificateConstraint.
- TargetsRole has one TargetsMetadata and one or more Delegations.
- Delegation has one TargetsMetadata and one CertificateConstraint.
- SnapshotRole has one SnapshotMetadata, and TimestampRole has one TimestampMetadata.
- Each of the SnapshotMetadata, TimestampMetadata, and TargetsMetadata have one DSSE.
By closely following the TUF concepts and architecture, this schema captures the relationships and data structures necessary to implement a secure and efficient TUF repository. It also enables modifications and extensions as needed, such as the support for an enhanced DSSE with certificate chains and Time-Stamp Authority (
```mermaid
erDiagram
Tennant {
uuid id
}
RootRole {
int threshold
set publickeys
}
TargetsRole {
int threshold
}
SnapshotRole {
int threshold
}
TimestampRole {
int threshold
}
CertificateConstraint {
set tsa_cert
int tsa_threshold
string ca_cert
string pub_key
uri issuer_uri
email ca_email
string subject
int max_ttl_secs
set san_dns
string spiffe_id
uri crl_url
}
SnapshotMetadata {
string version
string expires
}
TimestampMetadata {
string version
string expires
}
TargetsMetadata {
string version
string expires
}
DSSE {
string gitoid
}
Delegation {
string repopath
}
RootRole ||--o{ CertificateConstraint : has
RootRole ||--o{ TargetsRole : has
RootRole ||--o{ SnapshotRole : has
RootRole ||--o{ TimestampRole : has
TargetsRole ||--o{ TargetsMetadata : has
TargetsRole ||--o{ Delegation : has
Delegation ||--o{ TargetsMetadata : has
Delegation ||--o{ CertificateConstraint : has
RootRole ||--o{ CertificateConstraint : has
TargetsRole ||--o{ CertificateConstraint : has
SnapshotRole ||--o{ CertificateConstraint : has
TimestampRole ||--o{ CertificateConstraint : has
Tennant ||--o{ RootRole : has
SnapshotRole ||--o{ SnapshotMetadata : has
TimestampRole ||--o{ TimestampMetadata : has
SnapshotMetadata ||--o{ DSSE : has
TimestampMetadata ||--o{ DSSE : has
TargetsMetadata ||--o{ DSSE : has
```
### Targets Metadata Schema
The targets Metadata file will be an in-toto attestation in a DSSE envelope. The subjects will
include all of the target names (repo+ref) in this example as well as the keyids of the authorized signers.
```
{
"_type": "targets",
"spec_version": "1.0.0",
"version": 1,
"expires": "2023-06-07T00:00:00Z",
"targets": {
"{{repo+ref}}": {
"length": 123,
"hashes": {
"sha256": "6dcd4ce23d88e2ee95838f7b014b6284a5d1286530b9cfd226a61a89f0a46456"
},
"custom": {
"policy_version": 1.0.1
}
},
"{{repo+ref}}": {
"length": 456,
"hashes": {
"sha256": "a7f5f3542bd5f8a2a67a1e3c77a6681919e5e04f7362c6a0a7e8e20adc876695"
},
"custom": {
"policy_version": 2.0.3
}
}
// other target files...
},
"delegations": {
"keys": {
"roots": {
"root_cert_key_id": {
"certificate": "base64-encoded root certificate PEM block",
"intermediates": [
"base64-encoded intermediate certificate PEM block"
]
}
// Additional trusted root certificates...
},
"publickeys": {
"public_key_id": {
"keyid": "sha256sum of the public key",
"key": "base64-encoded public key"
}
// Additional trusted public keys...
},
"timestampauthorities": {
"timestamp_root_cert_key_id": {
"certificate": "base64-encoded root certificate PEM block",
"intermediates": [
"base64-encoded intermediate certificate PEM block"
]
}
// Additional trusted timestamp authority root certificates...
},
"certConstraints": {
"root_cert_key_id": {
"type": "root",
"certConstraint": {
"commonname": "Common Name",
"dnsnames": ["example.com", "*.example.com"],
"emails": ["email@example.com"],
"organizations": ["Organization"],
"uris": ["https://example.com"],
"roots": ["root_cert_key_id", "other_root_cert_key_id"]
}
}
}
}
}
}
```