summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Pollan <meskio@sindominio.net>2014-07-11 13:02:26 -0500
committerRuben Pollan <meskio@sindominio.net>2014-07-11 15:09:21 -0500
commit4f2da1b684fde6c0b452f79883b87ca057313cc7 (patch)
tree1c4950a97549fa03b32aab1bc0c81ba45cc9dff3
parent07686dfa07b03d5faa609a1554894aadf275a738 (diff)
Tools for TUF repository management
-rw-r--r--changes/feature-5864_create_TUF_release_tool1
-rwxr-xr-xpkg/tuf/init.py102
-rwxr-xr-xpkg/tuf/release.py114
3 files changed, 217 insertions, 0 deletions
diff --git a/changes/feature-5864_create_TUF_release_tool b/changes/feature-5864_create_TUF_release_tool
new file mode 100644
index 00000000..b9a4a3d5
--- /dev/null
+++ b/changes/feature-5864_create_TUF_release_tool
@@ -0,0 +1 @@
+- Add TUF init repository and release tools. Closes #5864.
diff --git a/pkg/tuf/init.py b/pkg/tuf/init.py
new file mode 100755
index 00000000..7300da0a
--- /dev/null
+++ b/pkg/tuf/init.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# init.py
+# Copyright (C) 2014 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Tool to initialize a TUF repo.
+
+The keys can be generated with:
+ openssl genrsa -des3 -out private.pem 4096
+The public key can be exported with:
+ openssl rsa -in private.pem -outform PEM -pubout -out public.pem
+"""
+
+import sys
+
+from tuf.repository_tool import create_new_repository
+from tuf.repository_tool import import_rsa_privatekey_from_file
+from tuf.repository_tool import import_rsa_publickey_from_file
+
+
+def usage():
+ print ("Usage: %s repo root_private_key root_pub_key targets_pub_key"
+ " timestamp_pub_key") % (sys.argv[0],)
+
+
+def main():
+ if len(sys.argv) < 6:
+ usage()
+ return
+
+ repo_path = sys.argv[1]
+ root_priv_path = sys.argv[2]
+ root_pub_path = sys.argv[3]
+ targets_pub_path = sys.argv[4]
+ timestamp_pub_path = sys.argv[5]
+ repo = Repo(repo_path, root_priv_path)
+ repo.build(root_pub_path, targets_pub_path, timestamp_pub_path)
+
+ print "%s/metadata.staged/root.json is ready" % (repo_path,)
+
+
+class Repo(object):
+ """
+ Repository builder class
+ """
+
+ def __init__(self, repo_path, key_path):
+ """
+ Constructor
+
+ :param repo_path: path where the repo lives
+ :type repo_path: str
+ :param key_path: path where the private root key lives
+ :type key_path: str
+ """
+ self._repo_path = repo_path
+ self._key = import_rsa_privatekey_from_file(key_path)
+
+ def build(self, root_pub_path, targets_pub_path, timestamp_pub_path):
+ """
+ Create a new repo
+
+ :param root_pub_path: path where the public root key lives
+ :type root_pub_path: str
+ :param targets_pub_path: path where the public targets key lives
+ :type targets_pub_path: str
+ :param timestamp_pub_path: path where the public timestamp key lives
+ :type timestamp_pub_path: str
+ """
+ repository = create_new_repository(self._repo_path)
+
+ pub_root_key = import_rsa_publickey_from_file(root_pub_path)
+ repository.root.add_verification_key(pub_root_key)
+ repository.root.load_signing_key(self._key)
+
+ pub_target_key = import_rsa_publickey_from_file(targets_pub_path)
+ repository.targets.add_verification_key(pub_target_key)
+ repository.snapshot.add_verification_key(pub_target_key)
+ repository.targets.compressions = ["gz"]
+ repository.snapshot.compressions = ["gz"]
+
+ pub_timestamp_key = import_rsa_publickey_from_file(timestamp_pub_path)
+ repository.timestamp.add_verification_key(pub_timestamp_key)
+
+ repository.write_partial()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/pkg/tuf/release.py b/pkg/tuf/release.py
new file mode 100755
index 00000000..c4abcd0d
--- /dev/null
+++ b/pkg/tuf/release.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# release.py
+# Copyright (C) 2014 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Tool to generate TUF related files after a release
+
+The 'repo' folder should contain two folders:
+ - 'metadata.staged' with all the jsons from the previows release
+ - 'targets' where the release targets are
+"""
+
+import datetime
+import os.path
+import sys
+
+from tuf.repository_tool import load_repository
+from tuf.repository_tool import import_rsa_privatekey_from_file
+from tuf.repository_tool import import_rsa_publickey_from_file
+
+"""
+Days until the expiration of targets.json and snapshot.json. After this ammount
+of days the TUF client won't accept this files.
+"""
+EXPIRATION_DAYS = 90
+
+
+def usage():
+ print "Usage: %s repo key" % (sys.argv[0],)
+
+
+def main():
+ if len(sys.argv) < 3:
+ usage()
+ return
+
+ repo_path = sys.argv[1]
+ key_path = sys.argv[2]
+ targets = Targets(repo_path, key_path)
+ targets.build()
+
+ print "%s/metadata.staged/(targets|snapshot).json[.gz] are ready" % \
+ (repo_path,)
+
+
+class Targets(object):
+ """
+ Targets builder class
+ """
+
+ def __init__(self, repo_path, key_path):
+ """
+ Constructor
+
+ :param repo_path: path where the repo lives
+ :type repo_path: str
+ :param key_path: path where the private targets key lives
+ :type key_path: str
+ """
+ self._repo_path = repo_path
+ self._key = import_rsa_privatekey_from_file(key_path)
+
+ def build(self):
+ """
+ Generate snapshot.json[.gz] and targets.json[.gz]
+ """
+ self._repo = load_repository(self._repo_path)
+ self._load_targets()
+
+ self._repo.targets.load_signing_key(self._key)
+ self._repo.snapshot.load_signing_key(self._key)
+ self._repo.targets.compressions = ["gz"]
+ self._repo.snapshot.compressions = ["gz"]
+ self._repo.snapshot.expiration = (
+ datetime.datetime.now() +
+ datetime.timedelta(days=EXPIRATION_DAYS))
+ self._repo.targets.expiration = (
+ datetime.datetime.now() +
+ datetime.timedelta(days=EXPIRATION_DAYS))
+ self._repo.write_partial()
+
+ def _load_targets(self):
+ """
+ Load a list of targets
+ """
+ targets_path = os.path.join(self._repo_path, 'targets')
+ target_list = self._repo.get_filepaths_in_directory(
+ targets_path,
+ recursive_walk=True,
+ followlinks=True)
+
+ for target in target_list:
+ octal_file_permissions = oct(os.stat(target).st_mode)[3:]
+ custom_file_permissions = {
+ 'file_permissions': octal_file_permissions
+ }
+ self._repo.targets.add_target(target, custom_file_permissions)
+
+
+if __name__ == "__main__":
+ main()