├── .github
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── README.md
├── final_product
└── .keep
├── functionary_bob
├── bob
└── bob.pub
├── functionary_carl
├── carl
└── carl.pub
├── owner_alice
├── alice
├── alice.pub
└── create_layout.py
├── requirements.txt
├── run_demo.py
└── run_demo_md.py
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: pip
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "10:00"
8 | open-pull-requests-limit: 10
9 |
10 | - package-ecosystem: "github-actions"
11 | directory: "/"
12 | schedule:
13 | interval: "daily"
14 | time: "10:00"
15 | open-pull-requests-limit: 10
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | push:
7 | branches:
8 | - main
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | test:
15 | name: Test
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version: ['3.8', '3.9', '3.10', '3.11']
20 | os: [ubuntu-latest]
21 | include:
22 | - python-version: "3.11"
23 | os: macos-latest
24 |
25 | runs-on: ${{ matrix.os }}
26 | steps:
27 | - name: Checkout demo
28 | uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
29 |
30 | - name: Set up Python ${{ matrix.python-version }}
31 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
32 | with:
33 | python-version: ${{ matrix.python-version }}
34 | cache: 'pip'
35 | cache-dependency-path: 'requirements.txt'
36 |
37 | - name: Install dependencies
38 | run: |
39 | python3 -m pip install -U pip
40 | python3 -m pip install -r requirements.txt
41 |
42 | - name: Run demo
43 | run: |
44 | python3 run_demo_md.py
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # in-toto demo [](https://github.com/in-toto/demo/actions/workflows/ci.yml)
2 |
3 | In this demo, we will use in-toto to secure a software supply chain with a very
4 | simple workflow. Bob is a developer for a project, Carl packages the software, and
5 | Alice oversees the project. So, using in-toto's names for the parties,
6 | Alice is the project owner - she creates and signs the software supply chain
7 | layout with her private key - and Bob and Carl are project functionaries -
8 | they carry out the steps of the software supply chain as defined in the layout.
9 |
10 | For the sake of demonstrating in-toto, we will have you run all parts of the
11 | software supply chain.
12 | This is, you will perform the commands on behalf of Alice, Bob and Carl as well
13 | as the client who verifies the final product.
14 |
15 |
16 | ## Download and setup in-toto on \*NIX (Linux, OS X, ..)
17 | __Virtual Environments (optional)__
18 |
19 | We highly recommend installing `in-toto` and its dependencies in a
20 | [`venv`](https://docs.python.org/3/library/venv.html) Python virtual
21 | environment. Just copy-paste the following snippet to create a virtual
22 | environment:
23 |
24 | ```bash
25 | # Create the virtual environment
26 | python -m venv in-toto-demo
27 |
28 | # Activate the virtual environment
29 | # This will add the prefix "(in-toto-demo)" to your shell prompt
30 | source in-toto-demo/bin/activate
31 | ```
32 |
33 | __Get demo files and install in-toto__
34 | ```bash
35 | # Fetch the demo repo using git
36 | git clone https://github.com/in-toto/demo.git
37 |
38 | # Change into the demo directory
39 | cd demo
40 |
41 | # Install a compatible version of in-toto
42 | pip install -r requirements.txt
43 | ```
44 | *Note: If you are having troubles installing in-toto, make sure you have all
45 | the system dependencies. See the [installation guide on
46 | in-toto.readthedocs.io](https://in-toto.readthedocs.io/en/latest/installing.html)
47 | for details.*
48 |
49 | Inside the demo directory you will find four directories: `owner_alice`,
50 | `functionary_bob`, `functionary_carl` and `final_product`. Alice, Bob and Carl
51 | already have RSA keys in each of their directories. This is what you see:
52 | ```bash
53 | tree # If you don't have tree, try 'find .' instead
54 | # the tree command gives you the following output
55 | # .
56 | # ├── README.md
57 | # ├── final_product
58 | # ├── functionary_bob
59 | # │ ├── bob
60 | # │ └── bob.pub
61 | # ├── functionary_carl
62 | # │ ├── carl
63 | # │ └── carl.pub
64 | # ├── owner_alice
65 | # │ ├── alice
66 | # │ ├── alice.pub
67 | # │ └── create_layout.py
68 | # ├── requirements.txt
69 | # ├── run_demo.py
70 | # └── run_demo_md.py
71 | ```
72 |
73 | ## Run the demo commands
74 | Note: if you don't want to type or copy & paste commands and would rather watch
75 | a script run through the commands, jump to [the last section of this document](#tired-of-copy-pasting-commands)
76 |
77 | ### Define software supply chain layout (Alice)
78 | First, we will need to define the software supply chain layout. To simplify this
79 | process, we provide a script that generates a simple layout for the purpose of
80 | the demo.
81 |
82 | In this software supply chain layout, we have Alice, who is the project
83 | owner that creates the layout, Bob, who clones the project's repo and
84 | performs some pre-packaging editing (update version number), and Carl, who uses
85 | `tar` to package the project sources into a tarball, which
86 | together with the in-toto metadata composes the final product that will
87 | eventually be installed and verified by the end user.
88 |
89 | ```shell
90 | # Create and sign the software supply chain layout on behalf of Alice
91 | cd owner_alice
92 | python create_layout.py
93 | ```
94 | The script will create a layout, add Bob's and Carl's public keys (fetched from
95 | their directories), sign it with Alice's private key and dump it to `root.layout`.
96 | In `root.layout`, you will find that (besides the signature and other information)
97 | there are three steps, `clone`, `update-version` and `package`, that
98 | the functionaries Bob and Carl, identified by their public keys, are authorized
99 | to perform.
100 |
101 | ### Clone project source code (Bob)
102 | Now, we will take the role of the functionary Bob and perform the step
103 | `clone` on his behalf, that is we use in-toto to clone the project repo from GitHub and
104 | record metadata for what we do. Execute the following commands to change to Bob's
105 | directory and perform the step.
106 |
107 | ```shell
108 | cd ../functionary_bob
109 | in-toto-run --step-name clone --use-dsse --products demo-project/foo.py --signing-key bob -- git clone https://github.com/in-toto/demo-project.git
110 | ```
111 |
112 | Here is what happens behind the scenes:
113 | 1. In-toto wraps the command `git clone https://github.com/in-toto/demo-project.git`,
114 | 1. hashes the contents of the source code, i.e. `demo-project/foo.py`,
115 | 1. adds the hash together with other information to a metadata file,
116 | 1. signs the metadata with Bob's private key, and
117 | 1. stores everything to `clone.[Bob's keyid].link`.
118 |
119 | ### Update version number (Bob)
120 | Before Carl packages the source code, Bob will update
121 | a version number hard-coded into `foo.py`. He does this using the `in-toto-record` command,
122 | which produces the same link metadata file as above but does not require Bob to wrap his action in a single command.
123 | So first Bob records the state of the files he will modify:
124 |
125 | ```shell
126 | # In functionary_bob directory
127 | in-toto-record start --step-name update-version --use-dsse --signing-key bob --materials demo-project/foo.py
128 | ```
129 |
130 | Then Bob uses an editor of his choice to update the version number in `demo-project/foo.py`, e.g.:
131 |
132 | ```shell
133 | sed -i.bak 's/v0/v1/' demo-project/foo.py && rm demo-project/foo.py.bak
134 | ```
135 |
136 | And finally he records the state of files after the modification and produces
137 | a link metadata file called `update-version.[Bob's keyid].link`.
138 | ```shell
139 | # In functionary_bob directory
140 | in-toto-record stop --step-name update-version --use-dsse --signing-key bob --products demo-project/foo.py
141 | ```
142 |
143 | Bob has done his work and can send over the sources to Carl, who will create
144 | the package for the user.
145 |
146 | ```shell
147 | # Bob has to send the update sources to Carl so that he can package them
148 | cp -r demo-project ../functionary_carl/
149 | ```
150 |
151 | ### Package (Carl)
152 | Now, we will perform Carl’s `package` step by executing the following commands
153 | to change to Carl's directory and create a package of the software project
154 |
155 | ```shell
156 | cd ../functionary_carl
157 | in-toto-run --step-name package --use-dsse --materials demo-project/foo.py --products demo-project.tar.gz --signing-key carl -- tar --exclude ".git" -zcvf demo-project.tar.gz demo-project
158 | ```
159 |
160 | This will create another step link metadata file, called `package.[Carl's keyid].link`.
161 | It's time to release our software now.
162 |
163 |
164 | ### Verify final product (client)
165 | Let's first copy all relevant files into the `final_product` that is
166 | our software package `demo-project.tar.gz` and the related metadata files `root.layout`,
167 | `clone.[Bob's keyid].link`, `update-version.[Bob's keyid].link` and `package.[Carl's keyid].link`:
168 | ```shell
169 | cd ..
170 | cp owner_alice/root.layout functionary_bob/clone.210dcc50.link functionary_bob/update-version.210dcc50.link functionary_carl/package.be06db20.link functionary_carl/demo-project.tar.gz final_product/
171 | ```
172 | And now run verification on behalf of the client:
173 | ```shell
174 | cd final_product
175 | # Fetch Alice's public key from a trusted source to verify the layout signature
176 | # Note: The functionary public keys are fetched from the layout
177 | cp ../owner_alice/alice.pub .
178 | in-toto-verify --layout root.layout --verification-keys alice.pub
179 | ```
180 | This command will verify that
181 | 1. the layout has not expired,
182 | 2. was signed with Alice’s private key,
183 |
and that according to the definitions in the layout
184 | 3. each step was performed and signed by the authorized functionary
185 | 4. the recorded materials and products follow the artifact rules and
186 | 5. the inspection `untar` finds what it expects.
187 |
188 |
189 | From it, you will see the meaningful output `PASSING` and a return value
190 | of `0`, that indicates verification worked out well:
191 | ```shell
192 | echo $?
193 | # should output 0
194 | ```
195 |
196 | ### Tampering with the software supply chain
197 | Now, let’s try to tamper with the software supply chain.
198 | Imagine that someone got a hold of the source code before Carl could package it.
199 | We will simulate this by changing `demo-project/foo.py` on Carl's machine
200 | (in `functionary_carl` directory) and then let Carl package and ship the
201 | malicious code.
202 |
203 | ```shell
204 | cd ../functionary_carl
205 | echo something evil >> demo-project/foo.py
206 | ```
207 | Carl thought that this is the genuine code he got from Bob and
208 | unwittingly packages the tampered version of foo.py
209 |
210 | ```shell
211 | in-toto-run --step-name package --use-dsse --materials demo-project/foo.py --products demo-project.tar.gz --signing-key carl -- tar --exclude ".git" -zcvf demo-project.tar.gz demo-project
212 | ```
213 | and ships everything out as final product to the client:
214 | ```shell
215 | cd ..
216 | cp owner_alice/root.layout functionary_bob/clone.210dcc50.link functionary_bob/update-version.210dcc50.link functionary_carl/package.be06db20.link functionary_carl/demo-project.tar.gz final_product/
217 | ```
218 |
219 | ### Verifying the malicious product
220 |
221 | ```shell
222 | cd final_product
223 | in-toto-verify --layout root.layout --verification-keys alice.pub
224 | ```
225 | This time, in-toto will detect that the product `foo.py` from Bob's `update-version`
226 | step was not used as material in Carl's `package` step (the verified hashes
227 | won't match) and therefore will fail verification an return a non-zero value:
228 | ```shell
229 | echo $?
230 | # should output 1
231 | ```
232 |
233 |
234 | ### Wrapping up
235 | Congratulations! You have completed the in-toto demo! This exercise shows a very
236 | simple case in how in-toto can protect the different steps within the software
237 | supply chain. More complex software supply chains that contain more steps can be
238 | created in a similar way. You can read more about what in-toto protects against
239 | and how to use it on [in-toto's Github page](https://in-toto.github.io/).
240 |
241 | ## Cleaning up and automated run through
242 | ### Clean slate
243 | If you want to run the demo again, you can use the following script to remove all the files you created above.
244 |
245 | ```bash
246 | cd .. # You have to be the demo directory
247 | python run_demo.py -c
248 | ```
249 |
250 | ### Tired of copy-pasting commands?
251 | The same script can be used to sequentially execute all commands listed above. Just change into the `demo` directory, run `python run_demo.py` without flags and observe the output.
252 |
253 | ```bash
254 | # In the demo directory
255 | python run_demo.py
256 | ```
257 |
--------------------------------------------------------------------------------
/final_product/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/in-toto/demo/3e2c5149e1fe926ae6deda9d64ebece15bd758f9/final_product/.keep
--------------------------------------------------------------------------------
/functionary_bob/bob:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | 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
39 | -----END RSA PRIVATE KEY-----
40 |
--------------------------------------------------------------------------------
/functionary_bob/bob.pub:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0Zfzonp3/FScaIP+KKuz
3 | B+OZNFpjbVGWjm3leqnFqHYLqrLcCw5KhlXpycJqoSvZBpO+PFCksUx8U/ryklHG
4 | VoDiB84pRkvZtBoVaA4b4IHDIhz1K5NqkJgieya4fwReTxmCW0a9gH7AnDicHBCX
5 | lzMxqEdt6OKMV5g4yjKaxf8lW72O1gSI46GSIToo+Z7UUgs3ofaM5UFIcczgCpUa
6 | 5kEKocB6cSZ9U8PKRLSs0xO0ROjrcOTsfxMs8eV4bsRCWY5mAq1WM9EHDSV9WO8g
7 | qrRmanC4enNqa8jU4O3zhgJVegP9A01r9AwNt6AqgPSikwhXN/P4v1FMYV+R6N3b
8 | S1lsVWRAnwBq5RFz5zVvcY88JEkHbrcBqP/A4909NXae1VMXmnoJb4EzGAkyUySB
9 | a+fHXAVJgzwyv3I48d/OIjH8NWcVmM/DQL7FtcJk3tp0YUjY5wNpcbQTnLzURtlU
10 | sd+MtGuvdlDxUUvtUYCIVKRdS8UzYnTPjI2xzeoSHZ2ZAgMBAAE=
11 | -----END PUBLIC KEY-----
12 |
--------------------------------------------------------------------------------
/functionary_carl/carl:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | 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
39 | -----END RSA PRIVATE KEY-----
40 |
--------------------------------------------------------------------------------
/functionary_carl/carl.pub:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAzgLBsMFSgwBiWTBmVsyW
3 | 5KbJwLFSodAzdUhU2Bq6SdRz/W6UOBGdojZXibxupjRtAaEQW/eXDe+1CbKg6ENZ
4 | Gt2D9HGFCQZgQS8ONgNDQGiNxgApMA0T21AaUhru0vEofzdN1DfEF4CAGv5AkcgK
5 | salhTyONervFIjFEdXGelFZ7dVMV3Pp5WkZPG0jFQWjnmDZhUrtSxEtqbVghc3kK
6 | AUj9Ll/3jyi2wS92Z1j5ueN8X62hWX2xBqQ6nViOMzdujkoiYCRSwuMLRqzW2CbT
7 | L8hF1+S5KWKFzxl5sCVfpPe7V5HkgEHjwCILXTbCn2fCMKlaSbJ/MG2lW7qSY2Ro
8 | wVXWkp1wDrsJ6Ii9f2dErv9vJeOVZeO9DsooQ5EuzLCfQLEU5mn7ul7bU7rFsb8J
9 | xYOeudkNBatnNCgVMAkmDPiNA7E33bmL5ARRwU0iZicsqLQR32pmwdap8PjofxqQ
10 | k7Gtvz/iYzaLrZv33cFWWTsEOqK1gKqigSqgW9T26wO9AgMBAAE=
11 | -----END PUBLIC KEY-----
12 |
--------------------------------------------------------------------------------
/owner_alice/alice:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | 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
39 | -----END RSA PRIVATE KEY-----
40 |
--------------------------------------------------------------------------------
/owner_alice/alice.pub:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxPX3kFs/z645x4UOC3KF
3 | Y3V80YQtKrp6YS3qU+Jlvx/XzK53lb4sCDRU9jqBBx3We45TmFUibroMd8tQXCUS
4 | e8gYCBUBqBmmz0dEHJYbW0tYF7IoapMIxhRYn76YqNdl1JoRTcmzIaOJ7QrHxQrS
5 | GpivvTm6kQ9WLeApG1GLYJ3C3Wl4bnsI1bKSv55Zi45/JawHzTzYUAIXX9qCd3Io
6 | HzDucz9IAj9Ookw0va/q9FjoPGrRB80IReVxLVnbo6pYJfu/O37jvEobHFa8ckHd
7 | YxUIg8wvkIOy1O3M74lBDm6CVI0ZO25xPlDB/4nHAE1PbA3aF3lw8JGuxLDsetxm
8 | fzgAleVt4vXLQiCrZaLf+0cM97JcT7wdHcbIvRLsij9LNP+2tWZgeZ/hIAOEdaDq
9 | cYANPDIAxfTvbe9I0sXrCtrLer1SS7GqUmdFCdkdun8erXdNF0ls9Rp4cbYhjdf3
10 | yMxdI/24LUOOQ71cHW3ITIDImm6I8KmrXFM2NewTARKfAgMBAAE=
11 | -----END PUBLIC KEY-----
12 |
--------------------------------------------------------------------------------
/owner_alice/create_layout.py:
--------------------------------------------------------------------------------
1 | from cryptography.hazmat.primitives.serialization import load_pem_private_key
2 | from securesystemslib.signer import CryptoSigner
3 | from in_toto.models.layout import Layout
4 | from in_toto.models.metadata import Envelope
5 | # https://github.com/in-toto/in-toto/issues/663
6 | from in_toto.models._signer import load_public_key_from_file
7 | def main():
8 | # Load Alice's private key to later sign the layout
9 | with open("alice", "rb") as f:
10 | key_alice = load_pem_private_key(f.read(), None)
11 |
12 | signer_alice = CryptoSigner(key_alice)
13 | # Fetch and load Bob's and Carl's public keys
14 | # to specify that they are authorized to perform certain step in the layout
15 | key_bob = load_public_key_from_file("../functionary_bob/bob.pub")
16 | key_carl = load_public_key_from_file("../functionary_carl/carl.pub")
17 |
18 | layout = Layout.read({
19 | "_type": "layout",
20 | "keys": {
21 | key_bob["keyid"]: key_bob,
22 | key_carl["keyid"]: key_carl,
23 | },
24 | "steps": [{
25 | "name": "clone",
26 | "expected_materials": [],
27 | "expected_products": [["CREATE", "demo-project/foo.py"], ["DISALLOW", "*"]],
28 | "pubkeys": [key_bob["keyid"]],
29 | "expected_command": [
30 | "git",
31 | "clone",
32 | "https://github.com/in-toto/demo-project.git"
33 | ],
34 | "threshold": 1,
35 | },{
36 | "name": "update-version",
37 | "expected_materials": [["MATCH", "demo-project/*", "WITH", "PRODUCTS",
38 | "FROM", "clone"], ["DISALLOW", "*"]],
39 | "expected_products": [["MODIFY", "demo-project/foo.py"], ["DISALLOW", "*"]],
40 | "pubkeys": [key_bob["keyid"]],
41 | "expected_command": [],
42 | "threshold": 1,
43 | },{
44 | "name": "package",
45 | "expected_materials": [
46 | ["MATCH", "demo-project/*", "WITH", "PRODUCTS", "FROM",
47 | "update-version"], ["DISALLOW", "*"],
48 | ],
49 | "expected_products": [
50 | ["CREATE", "demo-project.tar.gz"], ["DISALLOW", "*"],
51 | ],
52 | "pubkeys": [key_carl["keyid"]],
53 | "expected_command": [
54 | "tar",
55 | "--exclude",
56 | ".git",
57 | "-zcvf",
58 | "demo-project.tar.gz",
59 | "demo-project",
60 | ],
61 | "threshold": 1,
62 | }],
63 | "inspect": [{
64 | "name": "untar",
65 | "expected_materials": [
66 | ["MATCH", "demo-project.tar.gz", "WITH", "PRODUCTS", "FROM", "package"],
67 | # FIXME: If the routine running inspections would gather the
68 | # materials/products to record from the rules we wouldn't have to
69 | # ALLOW other files that we aren't interested in.
70 | ["ALLOW", ".keep"],
71 | ["ALLOW", "alice.pub"],
72 | ["ALLOW", "root.layout"],
73 | ["ALLOW", "*.link"],
74 | ["DISALLOW", "*"]
75 | ],
76 | "expected_products": [
77 | ["MATCH", "demo-project/foo.py", "WITH", "PRODUCTS", "FROM", "update-version"],
78 | # FIXME: See expected_materials above
79 | ["ALLOW", "demo-project/.git/*"],
80 | ["ALLOW", "demo-project.tar.gz"],
81 | ["ALLOW", ".keep"],
82 | ["ALLOW", "alice.pub"],
83 | ["ALLOW", "root.layout"],
84 | ["ALLOW", "*.link"],
85 | ["DISALLOW", "*"]
86 | ],
87 | "run": [
88 | "tar",
89 | "xzf",
90 | "demo-project.tar.gz",
91 | ]
92 | }],
93 | })
94 |
95 | metadata = Envelope.from_signable(layout)
96 |
97 | # Sign and dump layout to "root.layout"
98 | metadata.create_signature(signer_alice)
99 | metadata.dump("root.layout")
100 | print('Created demo in-toto layout as "root.layout".')
101 |
102 | if __name__ == '__main__':
103 | main()
104 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | in-toto==3.0.0
2 | cryptography==45.0.3
3 |
--------------------------------------------------------------------------------
/run_demo.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import shlex
4 | import subprocess
5 | import argparse
6 | import time
7 | from shutil import copyfile, copytree, rmtree
8 |
9 | NO_PROMPT = False
10 |
11 | def prompt_key(prompt):
12 | if NO_PROMPT:
13 | print("\n" + prompt)
14 | return
15 | inp = False
16 | while inp != "":
17 | try:
18 | inp = input("\n{} -- press any key to continue".format(prompt))
19 | except Exception:
20 | pass
21 |
22 | def supply_chain():
23 |
24 | prompt_key("Define supply chain layout (Alice)")
25 | os.chdir("owner_alice")
26 | create_layout_cmd = "python create_layout.py"
27 | print(create_layout_cmd)
28 | subprocess.call(shlex.split(create_layout_cmd))
29 |
30 | prompt_key("Clone source code (Bob)")
31 | os.chdir("../functionary_bob")
32 | clone_cmd = ("in-toto-run"
33 | " --verbose"
34 | " --use-dsse"
35 | " --step-name clone --products demo-project/foo.py"
36 | " --signing-key bob -- git clone https://github.com/in-toto/demo-project.git")
37 | print(clone_cmd)
38 | subprocess.call(shlex.split(clone_cmd))
39 |
40 | prompt_key("Update version number (Bob)")
41 | update_version_start_cmd = ("in-toto-record"
42 | " start"
43 | " --verbose"
44 | " --use-dsse"
45 | " --step-name update-version"
46 | " --signing-key bob"
47 | " --materials demo-project/foo.py")
48 |
49 | print(update_version_start_cmd)
50 | subprocess.call(shlex.split(update_version_start_cmd))
51 |
52 | update_version = "echo 'VERSION = \"foo-v1\"\n\nprint(\"Hello in-toto\")\n' > demo-project/foo.py"
53 | print(update_version)
54 | subprocess.call(update_version, shell=True)
55 |
56 | update_version_stop_cmd = ("in-toto-record"
57 | " stop"
58 | " --verbose"
59 | " --use-dsse"
60 | " --step-name update-version"
61 | " --signing-key bob"
62 | " --products demo-project/foo.py")
63 |
64 | print(update_version_stop_cmd)
65 | subprocess.call(shlex.split(update_version_stop_cmd))
66 |
67 | copytree("demo-project", "../functionary_carl/demo-project")
68 |
69 | prompt_key("Package (Carl)")
70 | os.chdir("../functionary_carl")
71 | package_cmd = ("in-toto-run"
72 | " --verbose"
73 | " --use-dsse"
74 | " --step-name package --materials demo-project/foo.py"
75 | " --products demo-project.tar.gz"
76 | " --signing-key carl --record-streams"
77 | " -- tar --exclude '.git' -zcvf demo-project.tar.gz demo-project")
78 | print(package_cmd)
79 | subprocess.call(shlex.split(package_cmd))
80 |
81 |
82 | prompt_key("Create final product")
83 | os.chdir("..")
84 | copyfile("owner_alice/root.layout", "final_product/root.layout")
85 | copyfile("functionary_bob/clone.210dcc50.link", "final_product/clone.210dcc50.link")
86 | copyfile("functionary_bob/update-version.210dcc50.link", "final_product/update-version.210dcc50.link")
87 | copyfile("functionary_carl/package.be06db20.link", "final_product/package.be06db20.link")
88 | copyfile("functionary_carl/demo-project.tar.gz", "final_product/demo-project.tar.gz")
89 |
90 |
91 | prompt_key("Verify final product (client)")
92 | os.chdir("final_product")
93 | copyfile("../owner_alice/alice.pub", "alice.pub")
94 | verify_cmd = ("in-toto-verify"
95 | " --verbose"
96 | " --layout root.layout"
97 | " --verification-keys alice.pub")
98 | print(verify_cmd)
99 | retval = subprocess.call(shlex.split(verify_cmd))
100 | print("Return value: " + str(retval))
101 |
102 |
103 |
104 |
105 | prompt_key("Tampering with the supply chain")
106 | os.chdir("../functionary_carl")
107 | tamper_cmd = "echo 'something evil' >> demo-project/foo.py"
108 | print(tamper_cmd)
109 | subprocess.call(tamper_cmd, shell=True)
110 |
111 |
112 | prompt_key("Package (Carl)")
113 | package_cmd = ("in-toto-run"
114 | " --verbose"
115 | " --use-dsse"
116 | " --step-name package --materials demo-project/foo.py"
117 | " --products demo-project.tar.gz"
118 | " --signing-key carl --record-streams"
119 | " -- tar --exclude '.git' -zcvf demo-project.tar.gz demo-project")
120 | print(package_cmd)
121 | subprocess.call(shlex.split(package_cmd))
122 |
123 |
124 | prompt_key("Create final product")
125 | os.chdir("..")
126 | copyfile("owner_alice/root.layout", "final_product/root.layout")
127 | copyfile("functionary_bob/clone.210dcc50.link", "final_product/clone.210dcc50.link")
128 | copyfile("functionary_bob/update-version.210dcc50.link", "final_product/update-version.210dcc50.link")
129 | copyfile("functionary_carl/package.be06db20.link", "final_product/package.be06db20.link")
130 | copyfile("functionary_carl/demo-project.tar.gz", "final_product/demo-project.tar.gz")
131 |
132 |
133 | prompt_key("Verify final product (client)")
134 | os.chdir("final_product")
135 | copyfile("../owner_alice/alice.pub", "alice.pub")
136 | verify_cmd = ("in-toto-verify"
137 | " --verbose"
138 | " --layout root.layout"
139 | " --verification-keys alice.pub")
140 |
141 | print(verify_cmd)
142 | retval = subprocess.call(shlex.split(verify_cmd))
143 | print("Return value: " + str(retval))
144 |
145 |
146 | def main():
147 | parser = argparse.ArgumentParser()
148 | parser.add_argument("-n", "--no-prompt", help="No prompt.",
149 | action="store_true")
150 | parser.add_argument("-c", "--clean", help="Remove files created during demo.",
151 | action="store_true")
152 | args = parser.parse_args()
153 |
154 | if args.clean:
155 | files_to_delete = [
156 | "owner_alice/root.layout",
157 | "functionary_bob/clone.210dcc50.link",
158 | "functionary_bob/update-version.210dcc50.link",
159 | "functionary_bob/demo-project",
160 | "functionary_carl/package.be06db20.link",
161 | "functionary_carl/demo-project.tar.gz",
162 | "functionary_carl/demo-project",
163 | "final_product/alice.pub",
164 | "final_product/demo-project.tar.gz",
165 | "final_product/package.be06db20.link",
166 | "final_product/clone.210dcc50.link",
167 | "final_product/update-version.210dcc50.link",
168 | "final_product/untar.link",
169 | "final_product/root.layout",
170 | "final_product/demo-project",
171 | ]
172 |
173 | for path in files_to_delete:
174 | if os.path.isfile(path):
175 | os.remove(path)
176 | elif os.path.isdir(path):
177 | rmtree(path)
178 |
179 | sys.exit(0)
180 | if args.no_prompt:
181 | global NO_PROMPT
182 | NO_PROMPT = True
183 |
184 |
185 | supply_chain()
186 |
187 | if __name__ == '__main__':
188 | main()
189 |
--------------------------------------------------------------------------------
/run_demo_md.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | run_demo_md.py
4 |
5 |
6 | Lukas Puehringer
7 |
8 |
9 | Jul 17, 2019
10 |
11 |
12 | Provides a script that extracts the demo code snippets from README.md and
13 | runs them in a shell, raising `SystemExit`, if the output is not as expected.
14 |
15 | virtualenv setup and installation of in-toto, as described in the demo
16 | instructions, is not performed by this script and must be done before running
17 | it. Snippets are run in a temporary directory, which is removed afterwards.
18 |
19 | NOTE: Currently, the script runs all snippets marked as `shell` snippets (see
20 | `SNIPPET_PATTERN`). To exclude a snippet from execution it must be marked as
21 | something else (e.g. `bash` to get the same syntax highlighting).
22 |
23 | """
24 | import os
25 | import re
26 | import shutil
27 | import sys
28 | import tempfile
29 | import difflib
30 | import subprocess
31 |
32 | # The file pointed to by `INSTRUCTIONS_FN` contains `shell` code snippets that
33 | # may be extracted using the regex defined in `SNIPPET_PATTERN`, and executed
34 | # to generate a combined stdout/stderr equal to `EXPECTED_STDOUT`.
35 | INSTRUCTIONS_FN = "README.md"
36 | SNIPPET_PATTERN = r"```shell\n([\s\S]*?)\n```"
37 |
38 | EXPECTED_STDOUT = \
39 | """+ cd owner_alice
40 | + python create_layout.py
41 | Created demo in-toto layout as "root.layout".
42 | + cd ../functionary_bob
43 | + in-toto-run --step-name clone --use-dsse --products demo-project/foo.py --signing-key bob -- git clone https://github.com/in-toto/demo-project.git
44 | + in-toto-record start --step-name update-version --use-dsse --signing-key bob --materials demo-project/foo.py
45 | + sed -i.bak s/v0/v1/ demo-project/foo.py
46 | + rm demo-project/foo.py.bak
47 | + in-toto-record stop --step-name update-version --use-dsse --signing-key bob --products demo-project/foo.py
48 | + cp -r demo-project ../functionary_carl/
49 | + cd ../functionary_carl
50 | + in-toto-run --step-name package --use-dsse --materials demo-project/foo.py --products demo-project.tar.gz --signing-key carl -- tar --exclude .git -zcvf demo-project.tar.gz demo-project
51 | + cd ..
52 | + cp owner_alice/root.layout functionary_bob/clone.210dcc50.link functionary_bob/update-version.210dcc50.link functionary_carl/package.be06db20.link functionary_carl/demo-project.tar.gz final_product/
53 | + cd final_product
54 | + cp ../owner_alice/alice.pub .
55 | + in-toto-verify --layout root.layout --verification-keys alice.pub
56 | + echo 0
57 | 0
58 | + cd ../functionary_carl
59 | + echo something evil
60 | + in-toto-run --step-name package --use-dsse --materials demo-project/foo.py --products demo-project.tar.gz --signing-key carl -- tar --exclude .git -zcvf demo-project.tar.gz demo-project
61 | + cd ..
62 | + cp owner_alice/root.layout functionary_bob/clone.210dcc50.link functionary_bob/update-version.210dcc50.link functionary_carl/package.be06db20.link functionary_carl/demo-project.tar.gz final_product/
63 | + cd final_product
64 | + in-toto-verify --layout root.layout --verification-keys alice.pub
65 | (in-toto-verify) RuleVerificationError: 'DISALLOW *' matched the following artifacts: ['demo-project/foo.py']
66 | Full trace for 'expected_materials' of item 'package':
67 | Available materials (used for queue):
68 | ['demo-project/foo.py']
69 | Available products:
70 | ['demo-project.tar.gz']
71 | Queue after 'MATCH demo-project/* WITH PRODUCTS FROM update-version':
72 | ['demo-project/foo.py']
73 |
74 | + echo 1
75 | 1
76 | """
77 |
78 | # Setup a test directory with all necessary demo files and change into it. This
79 | # lets us easily clean up all the files created during the demo eventually.
80 | demo_dir = os.path.dirname(os.path.realpath(__file__))
81 | tmp_dir = os.path.realpath(tempfile.mkdtemp())
82 | test_dir = os.path.join(tmp_dir, os.path.basename(demo_dir))
83 | shutil.copytree(demo_dir, test_dir)
84 | os.chdir(test_dir)
85 |
86 | # Wrap test code in try/finally to always tear down test directory and files
87 | try:
88 | # Extract all shell code snippets from demo instructions
89 | with open(INSTRUCTIONS_FN) as fp:
90 | readme = fp.read()
91 | snippets = re.findall(SNIPPET_PATTERN, readme)
92 |
93 | # Create script from all snippets, with shell xtrace mode (set -x) for
94 | # detailed output and make sure that it has the expected prefix (PS4='+ ')
95 | script = "PS4='+ '\nset -x\n{}".format("\n".join(snippets))
96 |
97 | # Execute script in one shell so we can run commands like `cd`
98 | # NOTE: Would be nice to use `in_toto.process.run_duplicate_streams` to show
99 | # output in real time, but the method does not support the required kwargs.
100 | proc = subprocess.Popen(
101 | ["/bin/sh", "-c", script],
102 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
103 | universal_newlines=True)
104 | stdout, _ = proc.communicate()
105 |
106 | print(stdout)
107 |
108 | # Fail if the output is not what we expected
109 | if stdout != EXPECTED_STDOUT:
110 | difflist = list(difflib.Differ().compare(
111 | EXPECTED_STDOUT.splitlines(),
112 | stdout.splitlines()))
113 | raise SystemExit(
114 | "#### DIFFERENCE:\n\n{}\n\nDemo test failed due to unexpected output "
115 | "(see above). :(".format("\n".join(difflist)))
116 |
117 | print("{}\nDemo test ran as expected. :)".format(stdout))
118 |
119 | finally:
120 | # Change back to where we were in the beginning and tear down test directory
121 | os.chdir(demo_dir)
122 | shutil.rmtree(test_dir)
123 |
--------------------------------------------------------------------------------