Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
Copied most things from @SolidifiedRay's repo of the same name.
  • Loading branch information
alanssitis committed Oct 13, 2022
0 parents commit d5511e2
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
venv/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ITE-4-demo
39 changes: 39 additions & 0 deletions functionary_bob/bob
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
11 changes: 11 additions & 0 deletions functionary_bob/bob.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0Zfzonp3/FScaIP+KKuz
B+OZNFpjbVGWjm3leqnFqHYLqrLcCw5KhlXpycJqoSvZBpO+PFCksUx8U/ryklHG
VoDiB84pRkvZtBoVaA4b4IHDIhz1K5NqkJgieya4fwReTxmCW0a9gH7AnDicHBCX
lzMxqEdt6OKMV5g4yjKaxf8lW72O1gSI46GSIToo+Z7UUgs3ofaM5UFIcczgCpUa
5kEKocB6cSZ9U8PKRLSs0xO0ROjrcOTsfxMs8eV4bsRCWY5mAq1WM9EHDSV9WO8g
qrRmanC4enNqa8jU4O3zhgJVegP9A01r9AwNt6AqgPSikwhXN/P4v1FMYV+R6N3b
S1lsVWRAnwBq5RFz5zVvcY88JEkHbrcBqP/A4909NXae1VMXmnoJb4EzGAkyUySB
a+fHXAVJgzwyv3I48d/OIjH8NWcVmM/DQL7FtcJk3tp0YUjY5wNpcbQTnLzURtlU
sd+MtGuvdlDxUUvtUYCIVKRdS8UzYnTPjI2xzeoSHZ2ZAgMBAAE=
-----END PUBLIC KEY-----
39 changes: 39 additions & 0 deletions owner_alice/alice
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
11 changes: 11 additions & 0 deletions owner_alice/alice.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxPX3kFs/z645x4UOC3KF
Y3V80YQtKrp6YS3qU+Jlvx/XzK53lb4sCDRU9jqBBx3We45TmFUibroMd8tQXCUS
e8gYCBUBqBmmz0dEHJYbW0tYF7IoapMIxhRYn76YqNdl1JoRTcmzIaOJ7QrHxQrS
GpivvTm6kQ9WLeApG1GLYJ3C3Wl4bnsI1bKSv55Zi45/JawHzTzYUAIXX9qCd3Io
HzDucz9IAj9Ookw0va/q9FjoPGrRB80IReVxLVnbo6pYJfu/O37jvEobHFa8ckHd
YxUIg8wvkIOy1O3M74lBDm6CVI0ZO25xPlDB/4nHAE1PbA3aF3lw8JGuxLDsetxm
fzgAleVt4vXLQiCrZaLf+0cM97JcT7wdHcbIvRLsij9LNP+2tWZgeZ/hIAOEdaDq
cYANPDIAxfTvbe9I0sXrCtrLer1SS7GqUmdFCdkdun8erXdNF0ls9Rp4cbYhjdf3
yMxdI/24LUOOQ71cHW3ITIDImm6I8KmrXFM2NewTARKfAgMBAAE=
-----END PUBLIC KEY-----
57 changes: 57 additions & 0 deletions owner_alice/create_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from securesystemslib import interface
from in_toto.models.layout import Layout
from in_toto.models.metadata import Metablock

def main():
# Load Alice's private key to later sign the layout
key_alice = interface.import_rsa_privatekey_from_file("alice")
# Fetch and load Bob's and Carl's public keys
# to specify that they are authorized to perform certain step in the layout
key_bob = interface.import_rsa_publickey_from_file("../functionary_bob/bob.pub")

layout = Layout.read({
"_type": "layout",
"keys": {
key_bob["keyid"]: key_bob,
},
"steps": [{
"name": "update-version",
"expected_materials": [["ALLOW", "*"]],
"expected_products": [["MODIFY", "foo.py"]],
"pubkeys": [key_bob["keyid"]],
"expected_command": [],
"threshold": 1,
},{
"name": "pull-request",
"expected_materials": [["MATCH", "*", "WITH", "PRODUCTS", "FROM",
"update-version"]],
"expected_products": [["ALLOW", "*"]],
"pubkeys": [key_bob["keyid"]],
"expected_command": [],
"threshold": 1,
},{
"name": "merge-pr",
"expected_materials": [["ALLOW", "*"]],
"expected_products": [["ALLOW", "*"]],
"pubkeys": [key_alice["keyid"]],
"expected_command": [],
"threshold": 1,
},{
"name": "tag",
"expected_materials": [["ALLOW", "*"]],
"expected_products": [["ALLOW", "*"]],
"pubkeys": [key_alice["keyid"]],
"expected_command": [],
"threshold": 1,
}]
})

metadata = Metablock(signed=layout)

# Sign and dump layout to "root.layout"
metadata.sign(key_alice)
metadata.dump("root.layout")
print('Created demo in-toto layout as "root.layout".')

if __name__ == '__main__':
main()
11 changes: 11 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
attrs==22.1.0
cffi==1.15.1
cryptography==38.0.1
in-toto==1.2.0
iso8601==1.1.0
pathspec==0.10.1
pycparser==2.21
python-dateutil==2.8.2
securesystemslib==0.24.0
six==1.16.0
urllib3==1.26.12
51 changes: 51 additions & 0 deletions resolvers/container-build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
ITE-4:
ITE-4 Resolver for container GH action build.
"""

import json
import sys

import securesystemslib.formats
import securesystemslib.hash


def digest_image(standard, tag, digest_algorithm, digest_value,
hash_algorithms):
if standard not in ['docker', 'oci']:
sys.exit(f"{standard} is not a recognized container standard")

if not hash_algorithms:
hash_algorithms = ['sha256']

securesystemslib.formats.HASHALGORITHMS_SCHEMA.check_match(hash_algorithms)
hash_dict = {}

for algorithm in hash_algorithms:
digest_object = securesystemslib.hash.digest(
algorithm, securesystemslib.hash.DEFAULT_HASH_LIBRARY)

representation = {
f'{standard}://{tag}': {
digest_algorithm: digest_value
}
}

digest_object.update(
json.dumps(representation, sort_keys=True).encode())
hash_dict.update({algorithm: digest_object.hexdigest()})

return hash_dict


if __name__ == '__main__':
if len(sys.argv) != 4:
sys.exit(f'Error: Usage: {sys.argv[0]} <standard> <tag> <digest>')

digest_algorithm, digest_value = sys.argv[3].split(':')
hash_algorithms = ['sha256', 'sha512', 'md5']
generated_hash = digest_image(sys.argv[1], sys.argv[2], digest_algorithm,
digest_value, hash_algorithms)

print(json.dumps(generated_hash, indent=4))
133 changes: 133 additions & 0 deletions resolvers/github_resolvers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
ITE-4:
Github has many abstract entities such as Pull Requests and Commits. This code
helps provide attestationns about these artifacts by getting the hash from the
Github entities.
"""

from urllib3.util import Url
from urllib3 import PoolManager
import json
import hashlib


def _get_resolvable_url(generic_uri):
"""
Convert a generic uri to an API URL that represent a Github entity
We should consider what generic_url looks like. Here is a draft idea of what
it should look like.
github:org/repo:pr:number
github:org/repo:commit:id
Args:
generic_uri: a generic_uri representing a GitHub entity
Returns:
an API URL representing a GitHub entity
"""
uri_split = generic_uri.split(':')
path = ''

if uri_split[2] == 'pr':
path = 'repos/{}/pulls/{}'.format(uri_split[1], uri_split[3])
elif uri_split[2] == 'commit':
path = 'repos/{}/commits/{}'.format(uri_split[1], uri_split[3])

resolvable_url = Url(scheme='https', host='api.github.com', path=path)

return resolvable_url


def _hash_review_representation(review):
'''
Capture representative fields in a review and return its hash. We may want to
be able to retrieve each status of the comment, such as CHANGES_REQUESTED and
APPROVED. We may also want to know who reviewed the PR and approved it.
some possible policies of reviews:
- Reviews should be done by authorized personnel (such as the memeber of the
organization)
- The code should not be pushed unless it has an APPROVED state
We can incorporate ITE-4 there for review attestations. This could be a part
of the statement's subject.
Args:
Review response data from Github API calls
Returns:
A hash that represent a Github review
'''
review_representation = {}
review_representation['id'] = review['id']
review_representation['author'] = review['user']['login']
review_representation['author_association'] = review['author_association']
review_representation['state'] = review['state']

dhash = hashlib.sha256()
encoded = json.dumps(review_representation, sort_keys=True).encode()
dhash.update(encoded)
hash_artifact = dhash.hexdigest()

return hash_artifact


def get_hashable_representation(generic_uri):
"""
Obtain a dict that helps provide attestationns about a GitHub entity
Args:
generic_uri: a generic_uri representing a GitHub entity
Returns:
A dictionary that represent a Github enitty
"""
resolvable_url = _get_resolvable_url(generic_uri)
github_entity_type = generic_uri.split(':')[2]

http = PoolManager()

response = http.request('GET', str(resolvable_url),
headers={'User-Agent': 'in-toto Reference Implementation'})
response_data = json.loads(response.data)

representation_object = {}
representation_object['type'] = github_entity_type

if github_entity_type == 'commit':
representation_object['commit_id'] = response_data['sha']
representation_object['author'] = response_data['author']['login']
representation_object['tree'] = response_data['commit']['tree']['sha']

return representation_object

elif github_entity_type == 'pr':
representation_object['user'] = response_data['user']['login']
representation_object['head'] = response_data['head']['label']
representation_object['base'] = response_data['base']['label']

representation_object['commits'] = []
commits_url = response_data['commits_url']
commits_api_response = http.request('GET', str(commits_url),
headers={'User-Agent': 'in-toto Reference Implementation'})
commits_response_data = json.loads(commits_api_response.data)
for commit in commits_response_data:
representation_object['commits'].append(commit['sha'])

representation_object['reviews'] = []
review_url = str(resolvable_url) + '/reviews'
review_api_response = http.request('GET', review_url,
headers={'User-Agent': 'in-toto Reference Implementation'})
review_response_data = json.loads(review_api_response.data)
for review in review_response_data:
representation_object['reviews'].append(
_hash_review_representation(review))

def hash_artifacts(generic_url):
"""
Obtain a hash from a GitHub abstract entity
Args:
generic_uri: a generic_uri representing a GitHub entity
Returns:
A hash that represent a Github enitty
"""
representation_object = get_hashable_representation(generic_url)

dhash = hashlib.sha256()
encoded = json.dumps(representation_object, sort_keys=True).encode()
dhash.update(encoded)

hash_artifacts = dhash.hexdigest()
return hash_artifacts

0 comments on commit d5511e2

Please sign in to comment.