aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS6
-rw-r--r--bpkg/auth96
-rw-r--r--bpkg/auth.cxx815
-rw-r--r--bpkg/buildfile2
-rw-r--r--bpkg/cfg-create.cxx4
-rw-r--r--bpkg/cfg-fetch.cxx52
-rw-r--r--bpkg/common.cli57
-rw-r--r--bpkg/database.cxx11
-rw-r--r--bpkg/fetch37
-rw-r--r--bpkg/fetch.cxx101
-rw-r--r--bpkg/openssl31
-rw-r--r--bpkg/openssl.cxx59
-rw-r--r--bpkg/options-types18
-rw-r--r--bpkg/package116
-rw-r--r--bpkg/package.cxx16
-rw-r--r--bpkg/package.xml13
-rw-r--r--bpkg/rep-create.cli17
-rw-r--r--bpkg/rep-create.cxx50
-rw-r--r--bpkg/rep-info.cli17
-rw-r--r--bpkg/rep-info.cxx61
-rw-r--r--bpkg/types-parsers8
-rw-r--r--bpkg/types-parsers.cxx20
-rw-r--r--bpkg/utility2
-rwxr-xr-xtests/cert.sh46
-rw-r--r--tests/default-openssl.cnf22
-rw-r--r--tests/key.pem51
-rw-r--r--tests/mismatch-openssl.cnf22
-rw-r--r--tests/noemail-openssl.cnf20
-rw-r--r--tests/pkg/.gitignore2
l---------tests/pkg/1/build2.org/auth/create-expired/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/fetch/t1/libfoo-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/auth/create-expired/repositories33
l---------tests/pkg/1/build2.org/auth/create-noemail/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t1/libfoo-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/auth/create-noemail/repositories33
l---------tests/pkg/1/build2.org/auth/expired/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t2/libfoo-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/auth/expired/packages11
-rw-r--r--tests/pkg/1/build2.org/auth/expired/repositories33
-rw-r--r--tests/pkg/1/build2.org/auth/expired/signature13
-rw-r--r--tests/pkg/1/build2.org/auth/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/depend/stable/libfoo-1.0.0.tar.gz)bin348 -> 348 bytes
l---------tests/pkg/1/build2.org/auth/name-mismatch/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t4c/libfoo-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/auth/name-mismatch/repositories34
l---------tests/pkg/1/build2.org/auth/sha256sum-mismatch/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/auth/sha256sum-mismatch/repositories33
l---------tests/pkg/1/build2.org/auth/signature-mismatch/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/auth/signature-mismatch/repositories33
l---------tests/pkg/1/build2.org/auth/signed/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/auth/signed/repositories33
l---------tests/pkg/1/build2.org/auth/unsigned1/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/auth/unsigned1/repositories (renamed from tests/repository/1/depend/stable/repositories)0
l---------tests/pkg/1/build2.org/auth/unsigned2/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/auth/unsigned2/repositories (renamed from tests/repository/1/fetch/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/bar/stable/libbar-1.0.0.tar.gz (renamed from tests/repository/1/common/bar/stable/libbar-1.0.0.tar.gz)bin957 -> 957 bytes
-rw-r--r--tests/pkg/1/build2.org/common/bar/stable/repositories (renamed from tests/repository/1/common/bar/stable/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/bar/testing/libbar-1.1.0.tar.gz (renamed from tests/repository/1/common/bar/testing/libbar-1.1.0.tar.gz)bin959 -> 959 bytes
-rw-r--r--tests/pkg/1/build2.org/common/bar/testing/repositories (renamed from tests/repository/1/common/bar/testing/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/bar/unstable/libbar-1.1.1.tar.gz (renamed from tests/repository/1/common/bar/unstable/libbar-1.1.1.tar.gz)bin1189 -> 1189 bytes
-rw-r--r--tests/pkg/1/build2.org/common/bar/unstable/repositories (renamed from tests/repository/1/common/bar/unstable/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/foo/stable/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/common/foo/stable/libfoo-1.0.0.tar.gz)bin881 -> 881 bytes
-rw-r--r--tests/pkg/1/build2.org/common/foo/stable/repositories (renamed from tests/repository/1/common/foo/stable/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/foo/testing/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/common/foo/testing/libfoo-1.1.0.tar.gz)bin874 -> 874 bytes
-rw-r--r--tests/pkg/1/build2.org/common/foo/testing/repositories (renamed from tests/repository/1/common/foo/testing/repositories)0
-rw-r--r--tests/pkg/1/build2.org/common/hello/libhello-1.0.0+1.tar.gz (renamed from tests/repository/1/common/hello/libhello-1.0.0+1.tar.gz)bin1489 -> 1489 bytes
-rw-r--r--tests/pkg/1/build2.org/common/hello/repositories36
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/bootstrap.build (renamed from tests/repository/1/common/libhello-1.0.0+1/build/bootstrap.build)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/export.build (renamed from tests/repository/1/common/libhello-1.0.0+1/build/export.build)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/root.build (renamed from tests/repository/1/common/libhello-1.0.0+1/build/root.build)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/buildfile (renamed from tests/repository/1/common/libhello-1.0.0+1/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/buildfile (renamed from tests/repository/1/common/libhello-1.0.0+1/hello/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello (renamed from tests/repository/1/common/libhello-1.0.0+1/hello/hello)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello.cxx (renamed from tests/repository/1/common/libhello-1.0.0+1/hello/hello.cxx)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/manifest (renamed from tests/repository/1/common/libhello-1.0.0+1/manifest)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/bootstrap.build (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/build/bootstrap.build)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/root.build (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/build/root.build)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/buildfile (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/buildfile (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/test/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/driver.cxx (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/test/driver.cxx)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/test.out (renamed from tests/repository/1/common/libhello-1.0.0+1/tests/test/test.out)0
-rw-r--r--tests/pkg/1/build2.org/common/libhello-1.0.0+1/version (renamed from tests/repository/1/common/libhello-1.0.0+1/version)0
-rw-r--r--tests/pkg/1/build2.org/common/not-a-package.tar.gz (renamed from tests/repository/1/common/not-a-package.tar.gz)bin259 -> 259 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libbar-1.0.0.tar.gz (renamed from tests/repository/1/depend/stable/libbar-1.0.0.tar.gz)bin354 -> 354 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libbar-1.1.0.tar.gz (renamed from tests/repository/1/depend/stable/libbar-1.1.0.tar.gz)bin359 -> 359 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libbar-1.2.0.tar.gz (renamed from tests/repository/1/depend/stable/libbar-1.2.0.tar.gz)bin372 -> 372 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libbar-1.3.0.tar.gz (renamed from tests/repository/1/depend/stable/libbar-1.3.0.tar.gz)bin378 -> 378 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/fetch/libfoo-1.0.0.tar.gz)bin348 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/depend/stable/libfoo-1.1.0.tar.gz)bin349 -> 349 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/libfoo-1.2.0.tar.gz (renamed from tests/repository/1/depend/stable/libfoo-1.2.0.tar.gz)bin348 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/depend/stable/repositories (renamed from tests/repository/1/satisfy/repositories)0
-rw-r--r--tests/pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/libfoo-1.0.0.tar.gz)bin348 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/fetch/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/fetch/libfoo-1.1.0.tar.gz)bin349 -> 349 bytes
-rw-r--r--tests/pkg/1/build2.org/fetch/libfoo-1.1.0/build/bootstrap.build (renamed from tests/repository/1/fetch/libfoo-1.1.0/build/bootstrap.build)0
-rw-r--r--tests/pkg/1/build2.org/fetch/libfoo-1.1.0/buildfile (renamed from tests/repository/1/fetch/libfoo-1.1.0/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/fetch/libfoo-1.1.0/manifest (renamed from tests/repository/1/fetch/libfoo-1.1.0/manifest)0
-rw-r--r--tests/pkg/1/build2.org/fetch/repositories (renamed from tests/repository/1/status/stable/repositories)0
l---------tests/pkg/1/build2.org/fetch/t1/libfoo-1.0.0.tar.gz1
l---------tests/pkg/1/build2.org/fetch/t1/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/fetch/t1/libfoo-1.1.0.tar.gz)0
l---------tests/pkg/1/build2.org/fetch/t1/repositories (renamed from tests/repository/1/fetch/t1/repositories)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbar-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/libbar-1.0.0.tar.gz)bin356 -> 356 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbar-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/libbar-1.1.0.tar.gz)bin360 -> 360 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbar-1.2.0.tar.gz (renamed from tests/repository/1/satisfy/libbar-1.2.0.tar.gz)bin348 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbaz-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/libbaz-1.0.0.tar.gz)bin359 -> 359 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbaz-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/libbaz-1.1.0.tar.gz)bin363 -> 363 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libbiz-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/libbiz-1.0.0.tar.gz)bin366 -> 366 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz (renamed from tests/repository/1/satisfy/libfoo-0.0.0.tar.gz)bin348 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gzbin0 -> 348 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/libfoo-1.1.0.tar.gz)bin349 -> 349 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/build/bootstrap.build (renamed from tests/repository/1/satisfy/libfoo-1.1.0/build/bootstrap.build)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/buildfile (renamed from tests/repository/1/satisfy/libfoo-1.1.0/buildfile)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/manifest (renamed from tests/repository/1/satisfy/libfoo-1.1.0/manifest)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfoo-1.2.0.tar.gz (renamed from tests/repository/1/satisfy/libfoo-1.2.0.tar.gz)bin350 -> 350 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/libfox-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/libfox-1.0.0.tar.gz)bin349 -> 349 bytes
-rw-r--r--tests/pkg/1/build2.org/satisfy/repositories1
l---------tests/pkg/1/build2.org/satisfy/t1/libfoo-1.0.0.tar.gz1
l---------tests/pkg/1/build2.org/satisfy/t1/repositories (renamed from tests/repository/1/satisfy/t1/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t2/libbar-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t2/libbar-1.0.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t2/libfoo-1.0.0.tar.gz1
l---------tests/pkg/1/build2.org/satisfy/t2/repositories (renamed from tests/repository/1/satisfy/t2/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t3/libbaz-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t3/libbaz-1.0.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t3/libfox-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t3/libfox-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/t3/repositories (renamed from tests/repository/1/satisfy/t3/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t4a/libfoo-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/t4a/libfoo-1.1.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t4a/repositories (renamed from tests/repository/1/satisfy/t4a/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t4b/libbar-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/t4b/libbar-1.1.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/t4b/repositories (renamed from tests/repository/1/satisfy/t4b/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t4c/libbaz-1.1.0.tar.gz (renamed from tests/repository/1/satisfy/t4c/libbaz-1.1.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t4c/libfoo-1.0.0.tar.gz1
-rw-r--r--tests/pkg/1/build2.org/satisfy/t4c/repositories (renamed from tests/repository/1/satisfy/t4c/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t4d/libbiz-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t4d/libbiz-1.0.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t4d/libfox-1.0.0.tar.gz (renamed from tests/repository/1/satisfy/t4d/libfox-1.0.0.tar.gz)0
-rw-r--r--tests/pkg/1/build2.org/satisfy/t4d/repositories (renamed from tests/repository/1/satisfy/t4d/repositories)0
l---------tests/pkg/1/build2.org/satisfy/t5/libbar-1.2.0.tar.gz (renamed from tests/repository/1/satisfy/t5/libbar-1.2.0.tar.gz)0
l---------tests/pkg/1/build2.org/satisfy/t5/repositories (renamed from tests/repository/1/satisfy/t5/repositories)0
-rw-r--r--tests/pkg/1/build2.org/status/extra/libbar-1.1.0+1.tar.gz (renamed from tests/repository/1/status/extra/libbar-1.1.0+1.tar.gz)bin243 -> 243 bytes
-rw-r--r--tests/pkg/1/build2.org/status/extra/repositories (renamed from tests/repository/1/status/extra/repositories)0
-rw-r--r--tests/pkg/1/build2.org/status/stable/libbar-1.0.0.tar.gz (renamed from tests/repository/1/status/stable/libbar-1.0.0.tar.gz)bin241 -> 241 bytes
-rw-r--r--tests/pkg/1/build2.org/status/stable/libfoo-1.0.0.tar.gz (renamed from tests/repository/1/status/stable/libfoo-1.0.0.tar.gz)bin240 -> 240 bytes
-rw-r--r--tests/pkg/1/build2.org/status/stable/repositories1
-rw-r--r--tests/pkg/1/build2.org/status/testing/libbar-1.0.0+1.tar.gz (renamed from tests/repository/1/status/testing/libbar-1.0.0+1.tar.gz)bin243 -> 243 bytes
-rw-r--r--tests/pkg/1/build2.org/status/testing/libbar-1.1.0.tar.gz (renamed from tests/repository/1/status/testing/libbar-1.1.0.tar.gz)bin242 -> 242 bytes
-rw-r--r--tests/pkg/1/build2.org/status/testing/repositories (renamed from tests/repository/1/status/testing/repositories)0
-rw-r--r--tests/pkg/1/build2.org/status/unstable/libbar-2.0.0.tar.gz (renamed from tests/repository/1/status/unstable/libbar-2.0.0.tar.gz)bin245 -> 245 bytes
-rw-r--r--tests/pkg/1/build2.org/status/unstable/repositories (renamed from tests/repository/1/status/unstable/repositories)0
-rwxr-xr-xtests/pkg/publish (renamed from tests/repository/publish)3
-rw-r--r--tests/repository/.gitignore1
-rw-r--r--tests/repository/1/common/hello/repositories4
-rwxr-xr-xtests/test.sh398
144 files changed, 2254 insertions, 227 deletions
diff --git a/NEWS b/NEWS
index 7a15d9c..dcb6845 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Version 0.4.0
+
+ * Add support for repository authentication. The rep-create command can now
+ sign the repository, and cfg-fetch and rep-info commands can authenticate
+ the repository being fetched.
+
Version 0.3.0
* Command line options and arguments can now be specified in any order. This
diff --git a/bpkg/auth b/bpkg/auth
new file mode 100644
index 0000000..54071b7
--- /dev/null
+++ b/bpkg/auth
@@ -0,0 +1,96 @@
+// file : bpkg/auth -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_AUTH
+#define BPKG_AUTH
+
+#include <bpkg/manifest>
+
+#include <bpkg/types>
+#include <bpkg/utility>
+
+#include <bpkg/package>
+#include <bpkg/common-options>
+
+namespace bpkg
+{
+ // Authenticate a repository certificate. If the configuration directory is
+ // NULL, then perform without a certificate database. If it is empty, then
+ // check if the current working directory is a configuration. If it is, then
+ // use its certificate database. Otherwise, continue as if it was NULL. All
+ // other values (including '.') are assumed to be valid configuration paths
+ // and will be diagnosed if that's not the case.
+ //
+ // If the configuration is used, then check if we are already in transaction.
+ // If so, then assume the configuration database is already opened and use
+ // that. Otherwise, open the database and start a new transaction.
+ //
+ // Note that one drawback of doing this as part of an existing transaction
+ // is that if things go south and the transaction gets aborted, then all the
+ // user's confirmations will be lost. For example, cfg-fetch could fail
+ // because it was unable to fetch some prerequisite repositories.
+ //
+ shared_ptr<const certificate>
+ authenticate_certificate (const common_options&,
+ const dir_path* configuration,
+ const optional<string>& cert_pem,
+ const repository_location&);
+
+ // Authenticate a repository. First check that the certificate can be used
+ // to authenticate this repository by making sure their names match. Then
+ // recover the packages manifest file SHA256 checksum from the signature
+ // and compare the calculated checksum to the recovered one.
+ //
+ // If the configuration directory is NULL, then create a temporary
+ // certificate PEM file (cert_pem must be present). If the directory is
+ // empty, then check if the current working directory is a configuration.
+ // If it's not, then continue as if it was NULL (cert_pem must be present).
+ // If it is, then continue as if a valid configuration directory was
+ // specified. All other values (including '.') are assumed to be valid
+ // configuration paths and will be diagnosed if that's not the case. In the
+ // case of a valid configuration use the certificate PEM file from the
+ // configuration (the file is supposed to have been created by the preceding
+ // authenticate_certificate() call).
+ //
+ void
+ authenticate_repository (const common_options&,
+ const dir_path* configuration,
+ const optional<string>& cert_pem,
+ const certificate&,
+ const signature_manifest&,
+ const repository_location&);
+
+ // Sign a repository by calculating its packages manifest file signature.
+ // This is done by encrypting the file's SHA256 checksum with the repository
+ // certificate's private key and then base64-encoding the result. Issue
+ // diagnstics and fail if the certificate has expired, and issue a warning
+ // if it expires in less than 2 months. The repository argument is used for
+ // diagnostics only.
+ //
+ // Note that currently we don't check if the key matches the certificate. A
+ // relatively easy way to accomplish this would be to execute the following
+ // commands and match the results:
+ //
+ // openssl x509 -noout -modulus -in cert.pem
+ // openssl rsa -noout -modulus -in key.pem
+ //
+ // But taking into account that we need to be able to use custom engines to
+ // access keys, it seems to be impossible to provide the same additional
+ // openssl options to fit both the rsa and pkeyutl commands. The first would
+ // require "-engine pkcs11 -inform engine", while the second -- "-engine
+ // pkcs11 -keyform engine". Also it would require to enter the key password
+ // again, which is a showstopper. Maybe the easiest would be to recover the
+ // sum back from the signature using the certificate, and compare it with
+ // the original sum (like we do in authenticate_repository()). But that
+ // would require to temporarily save the certificate to file.
+ //
+ std::vector<char>
+ sign_repository (const common_options&,
+ const string& sha256sum,
+ const string& key_name, // --key option value
+ const string& cert_pem,
+ const dir_path& repository);
+}
+
+#endif // BPKG_AUTH
diff --git a/bpkg/auth.cxx b/bpkg/auth.cxx
new file mode 100644
index 0000000..5296bc8
--- /dev/null
+++ b/bpkg/auth.cxx
@@ -0,0 +1,815 @@
+// file : bpkg/auth.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/auth>
+
+#include <ratio>
+#include <limits> // numeric_limits
+#include <fstream>
+#include <cstring> // strlen(), strcmp()
+#include <iterator> // ostreambuf_iterator, istreambuf_iterator
+
+#include <butl/sha256>
+#include <butl/base64>
+#include <butl/process>
+#include <butl/fdstream>
+#include <butl/filesystem>
+
+#include <bpkg/openssl>
+#include <bpkg/package>
+#include <bpkg/package-odb>
+#include <bpkg/database>
+#include <bpkg/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ // Find the repository location prefix that ends with the version component.
+ // We consider all repositories under this location to be related.
+ //
+ static string
+ name_prefix (const repository_location& rl)
+ {
+ assert (rl.absolute () || rl.remote ());
+
+ // Construct the prefix as a relative repository location.
+ //
+ string p (".");
+ for (auto i (rl.path ().rbegin ()), e (rl.path ().rend ()); i != e; ++i)
+ {
+ const string& c (*i);
+ if (!c.empty () && c.find_first_not_of ("1234567890") == string::npos)
+ break;
+
+ p = "../" + p;
+ }
+
+ // If this is a remote location then use the canonical name prefix. For
+ // a local location this doesn't always work. Consider:
+ //
+ // .../pkg/1/build2.org/common/hello
+ //
+ // In this case we will end with an empty canonical name (because of
+ // the special pkg/1 treatment). So in case of local locations we will
+ // use the location rather than the name prefix.
+ //
+ if (rl.remote ())
+ return repository_location (p, rl).canonical_name ();
+ else
+ {
+ path lp (rl.path () / path (p));
+ lp.normalize ();
+ return lp.string ();
+ }
+ }
+
+ // Authenticate a dummy certificate. If trusted, it will authenticate all
+ // the (unsigned) repositories under the location prefix of up-to-the-
+ // version component.
+ //
+ static shared_ptr<certificate>
+ auth_dummy (const common_options& co,
+ const string& fp,
+ const repository_location& rl)
+ {
+ tracer trace ("auth_dummy");
+
+ shared_ptr<certificate> cert (
+ make_shared<certificate> (fp, name_prefix (rl)));
+
+ l4 ([&]{trace << "new cert: " << *cert;});
+
+ if (co.trust_yes ())
+ {
+ if (verb)
+ info << "trusting unsigned repository " << rl.canonical_name ();
+ }
+ else
+ {
+ (co.trust_no () ? error : warn) << "repository " << rl.canonical_name ()
+ << " is unsigned";
+ }
+
+ if (co.trust_no () ||
+ (!co.trust_yes () &&
+ !yn_prompt (
+ string ("continue without authenticating repositories at " +
+ cert->name + "? [y/N]").c_str (), 'n')))
+ throw failed ();
+
+ return cert;
+ }
+
+ // Calculate the real repository certificate fingerprint. Return the compact
+ // form (no colons, lower case).
+ //
+ static string
+ real_fingerprint (const common_options& co,
+ const string& pem,
+ const repository_location& rl)
+ {
+ tracer trace ("real_fingerprint");
+
+ try
+ {
+ process pr (start_openssl (
+ co, "x509", {"-sha256", "-noout", "-fingerprint"}, true, true));
+
+ ifdstream is (pr.in_ofd);
+ is.exceptions (ifdstream::badbit);
+
+ try
+ {
+ ofdstream os (pr.out_fd);
+ os.exceptions (ofdstream::badbit);
+ os << pem;
+ os.close ();
+
+ string s;
+ const size_t n (19);
+ if (!(getline (is, s) && s.size () > n &&
+ s.compare (0, n, "SHA256 Fingerprint=") == 0))
+ throw istream::failure ("");
+
+ string fp;
+
+ try
+ {
+ fp = fingerprint_to_sha256 (string (s, n));
+ }
+ catch (const invalid_argument&)
+ {
+ throw istream::failure ("");
+ }
+
+ is.close ();
+
+ if (pr.wait ())
+ return fp;
+
+ // Fall through.
+ //
+ }
+ catch (const istream::failure&)
+ {
+ // Child input writing or output reading error.
+ //
+ is.close ();
+
+ // Child exit status doesn't matter. Just wait for the process
+ // completion and fall through.
+ //
+ pr.wait ();
+ }
+
+ error << "unable to calculate certificate fingerprint for "
+ << rl.canonical_name ();
+
+ // Fall through.
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to calculate certificate fingerprint for "
+ << rl.canonical_name () << ": " << e.what ();
+
+ // Fall through.
+ }
+
+ throw failed ();
+ }
+
+ // Calculate the repository certificate fingerprint. Return the compact form
+ // (no colons, lower case).
+ //
+ static string
+ cert_fingerprint (const common_options& co,
+ const optional<string>& pem,
+ const repository_location& rl)
+ {
+ return pem
+ ? real_fingerprint (co, *pem, rl)
+ : sha256 (name_prefix (rl)).string ();
+ }
+
+ // Parse the PEM-encoded certificate representation.
+ //
+ static shared_ptr<certificate>
+ parse_cert (const common_options& co,
+ const string& fp,
+ const string& pem,
+ const string& repo)
+ {
+ tracer trace ("parse_cert");
+
+ try
+ {
+ // The order of the options we pass to openssl determines the order in
+ // which we get things in the output. And want we expect is this
+ // (leading space added):
+ //
+ // subject=
+ // CN=name:cppget.org
+ // O=Code Synthesis
+ // notBefore=Apr 7 12:20:58 2016 GMT
+ // notAfter=Apr 7 12:20:58 2017 GMT
+ // info@cppget.org
+ //
+ // The first line must be "subject=" (it cannot be omitted from the
+ // cert). After it we have one or more lines indented with four spaces
+ // that specify the components. We are interested in CN and O, though
+ // there could be others which we ignore. Then we must have the
+ // notBefore and notAfter dates, again they presumably must be there.
+ // The final line should be the email but will be silently missing if
+ // the cert has no email.
+ //
+ process pr (start_openssl (
+ co,
+ "x509",
+ {
+ "-noout",
+ "-subject",
+ "-dates",
+ "-email",
+ "-nameopt", "RFC2253,sep_multiline"
+ },
+ true,
+ true));
+
+ ifdstream is (pr.in_ofd);
+ is.exceptions (ifdstream::badbit);
+
+ try
+ {
+ ofdstream os (pr.out_fd);
+ os.exceptions (ofdstream::badbit);
+
+ // Reading from and writing to the child process standard streams from
+ // the same thread is generally a bad idea. Depending on the program
+ // implementation we can block on writing if the process input pipe
+ // buffer get filled. That can happen if the process do not read
+ // anymore, being blocked on writing to the filled output pipe, which
+ // get filled not being read on the other end.
+ //
+ // Fortunatelly openssl reads the certificate before performing any
+ // output.
+ //
+ os << pem;
+ os.close ();
+
+ auto bad_cert ([](const string& d) {throw invalid_argument (d);});
+
+ auto get = [&is, &trace] (string& s) -> bool
+ {
+ bool r (getline (is, s));
+ l6 ([&]{trace << s;});
+ return r;
+ };
+
+ string s;
+ if (!get (s) || s != "subject= ")
+ bad_cert ("no subject");
+
+ // Parse RDN (relative distinguished name).
+ //
+ auto parse_rdn = [&s, &bad_cert] (size_t o, const char* name) -> string
+ {
+ string r (s.substr (o));
+ if (r.empty ())
+ bad_cert (name + string (" is empty"));
+
+ return r;
+ };
+
+ auto parse_date = [&s](size_t o, const char* name) -> timestamp
+ {
+ // Certificate dates are internally represented as ASN.1
+ // GeneralizedTime and UTCTime
+ // (http://www.obj-sys.com/asn1tutorial/node14.html). They are
+ // printed by openssl in the 'MON DD HH:MM:SS[.fff][ GMT]' format.
+ // MON is a month abbreviated name (C locale), .fff is a fraction
+ // of a second expressed in milliseconds, timezone is either GMT or
+ // absent (means local time). Examples:
+ //
+ // Apr 11 10:20:02 2016 GMT
+ // Apr 11 10:20:02 2016
+ // Apr 11 10:20:02.123 2016 GMT
+ // Apr 11 10:20:02.123 2016
+ //
+ // We will require the date to be in GMT, as generally can not
+ // interpret the certificate origin local time. Note:
+ // openssl-generated certificate dates are always in GMT, and with
+ // milliseconds omitted.
+ //
+ try
+ {
+ // Assume the global locale is not changed, and still "C".
+ //
+ const char* end;
+ timestamp t (from_string (
+ s.c_str () + o, "%b %d %H:%M:%S%[.M] %Y", false, &end));
+
+ if (strcmp (end, " GMT") == 0)
+ return t;
+ }
+ catch (const system_error&)
+ {
+ }
+
+ throw invalid_argument ("invalid " + string (name) + " date");
+ };
+
+ string name;
+ string org;
+ while (get (s))
+ {
+ if (s.compare (0, 7, " CN=") == 0)
+ name = parse_rdn (7, "common name");
+ else if (s.compare (0, 6, " O=") == 0)
+ org = parse_rdn (6, "organization name");
+ else if (s.compare (0, 4, " ") != 0)
+ break; // End of the subject sub-lines.
+ }
+
+ if (name.empty ())
+ bad_cert ("no common name (CN)");
+
+ if (name.compare (0, 5, "name:") != 0)
+ bad_cert ("no 'name:' prefix in the common name (CN)");
+
+ name = name.substr (5);
+ if (name.empty ())
+ bad_cert ("no repository name in the common name (CN)");
+
+ if (org.empty ())
+ bad_cert ("no organization name (O)");
+
+ if (!is || s.compare (0, 10, "notBefore=") != 0)
+ bad_cert ("no start date");
+
+ timestamp not_before (parse_date (10, "start"));
+
+ if (!get (s) || s.compare (0, 9, "notAfter=") != 0)
+ bad_cert ("no end date");
+
+ timestamp not_after (parse_date (9, "end"));
+
+ if (not_before >= not_after)
+ bad_cert ("invalid date range");
+
+ string email;
+ if (!get (email) || email.empty ())
+ bad_cert ("no email");
+
+ // Ensure no data left in the stream.
+ //
+ if (is.peek () != ifdstream::traits_type::eof ())
+ bad_cert ("unexpected data");
+
+ is.close ();
+
+ shared_ptr<certificate> cert (
+ make_shared<certificate> (
+ fp,
+ move (name),
+ move (org),
+ move (email),
+ move (not_before),
+ move (not_after)));
+
+ if (pr.wait ())
+ return cert;
+
+ // Fall through.
+ //
+ }
+ catch (const istream::failure&)
+ {
+ // Child input writing or output reading error.
+ //
+ is.close ();
+
+ // Child exit status doesn't matter. Just wait for the process
+ // completion and fall through.
+ //
+ pr.wait ();
+ }
+ catch (const invalid_argument& e)
+ {
+ // Certificate parsing error. Skip until the end, not to offend the
+ // child with the broken pipe. Never knows how it will take it.
+ //
+ if (!is.eof ())
+ is.ignore (numeric_limits<streamsize>::max ());
+
+ is.close ();
+
+ // If the child exited with an error status, then omit any output
+ // parsing diagnostics since we were probably parsing garbage.
+ //
+ if (pr.wait ())
+ {
+ error << "invalid certificate for " << repo << ": " << e.what ();
+ throw failed ();
+ }
+
+ // Fall through.
+ }
+
+ error << "unable to parse certificate for " << repo;
+
+ // Fall through.
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to parse certificate for " << repo << ": " << e.what ();
+
+ // Fall through.
+ }
+
+ throw failed ();
+ }
+
+ // Verify the certificate (validity period and such).
+ //
+ static void
+ verify_cert (const certificate& cert, const repository_location& rl)
+ {
+ if (!cert.dummy ())
+ {
+ if (cert.expired ())
+ fail << "certificate for repository " << rl.canonical_name ()
+ << " has expired";
+ }
+ }
+
+ // Authenticate a real certificate.
+ //
+ static shared_ptr<certificate>
+ auth_real (const common_options& co,
+ const string& fp,
+ const string& pem,
+ const repository_location& rl)
+ {
+ tracer trace ("auth_real");
+
+ shared_ptr<certificate> cert (
+ parse_cert (co, fp, pem, rl.canonical_name ()));
+
+ l4 ([&]{trace << "new cert: " << *cert;});
+
+ verify_cert (*cert, rl);
+
+ string cert_fp (sha256_to_fingerprint (cert->fingerprint));
+
+ // @@ Is there a way to intercept CLI parsing for the specific option of
+ // the standard type to validate/convert the value? If there were, we could
+ // validate the option value converting fp to sha (internal representation
+ // of fp).
+ //
+ // @@ Not easily/cleanly. The best way is to derive a custom type which
+ // will probably be an overkill here.
+ //
+ bool trust (co.trust_yes () ||
+ co.trust ().find (cert_fp) != co.trust ().end ());
+
+ if (trust)
+ {
+ if (verb)
+ info << "trusting non-authenticated certificate for repository "
+ << rl.canonical_name ();
+
+ return cert;
+ }
+
+ (co.trust_no () ? error : warn)
+ << "authenticity of the certificate for repository "
+ << rl.canonical_name () << " cannot be established";
+
+ if (!co.trust_no () && verb)
+ {
+ text << "certificate is for " << cert->name << ", \""
+ << cert->organization << "\" <" << cert->email << ">";
+
+ text << "certificate SHA256 fingerprint is " << cert_fp;
+ }
+
+ if (co.trust_no () || !yn_prompt ("trust this certificate? [y/N]", 'n'))
+ throw failed ();
+
+ return cert;
+ }
+
+ static const dir_path certs_dir (".bpkg/certs");
+
+ // Authenticate a certificate with the database. First check if it is
+ // already authenticated. If not, authenticate and add to the database.
+ //
+ static shared_ptr<certificate>
+ auth_cert (const common_options& co,
+ const dir_path& conf,
+ database& db,
+ const optional<string>& pem,
+ const repository_location& rl)
+ {
+ tracer trace ("auth_cert");
+ tracer_guard tg (db, trace);
+
+ string fp (cert_fingerprint (co, pem, rl));
+ shared_ptr<certificate> cert (db.find<certificate> (fp));
+
+ if (cert != nullptr)
+ {
+ l4 ([&]{trace << "existing cert: " << *cert;});
+ verify_cert (*cert, rl);
+ return cert;
+ }
+
+ cert = pem ? auth_real (co, fp, *pem, rl) : auth_dummy (co, fp, rl);
+ db.persist (cert);
+
+ // Save the certificate file.
+ //
+ if (pem)
+ {
+ dir_path d (conf / certs_dir);
+ if (!dir_exists (d))
+ mk (d);
+
+ path f (d / path (fp + ".pem"));
+
+ try
+ {
+ ofstream ofs;
+ ofs.exceptions (ofstream::badbit | ofstream::failbit);
+ ofs.open (f.string ());
+ ofs << *pem;
+ }
+ catch (const ofstream::failure&)
+ {
+ fail << "unable to write certificate to " << f;
+ }
+ }
+
+ return cert;
+ }
+
+ static const dir_path current_dir (".");
+
+ shared_ptr<const certificate>
+ authenticate_certificate (const common_options& co,
+ const dir_path* conf,
+ const optional<string>& pem,
+ const repository_location& rl)
+ {
+ tracer trace ("authenticate_certificate");
+
+ if (co.trust_no () && co.trust_yes ())
+ fail << "--trust-yes and --trust-no are mutually exclusive";
+
+ if (conf != nullptr && conf->empty ())
+ conf = dir_exists (path (".bpkg")) ? &current_dir : nullptr;
+
+ assert (conf == nullptr || !conf->empty ());
+
+ shared_ptr<certificate> r;
+
+ if (conf == nullptr)
+ {
+ // If we have no configuration, go straight to authenticating a new
+ // certificate.
+ //
+ string fp (cert_fingerprint (co, pem, rl));
+ r = pem ? auth_real (co, fp, *pem, rl) : auth_dummy (co, fp, rl);
+ }
+ else if (transaction::has_current ())
+ {
+ r = auth_cert (co, *conf, transaction::current ().database (), pem, rl);
+ }
+ else
+ {
+ database db (open (*conf, trace));
+ transaction t (db.begin ());
+ r = auth_cert (co, *conf, db, pem, rl);
+ t.commit ();
+ }
+
+ return r;
+ }
+
+ void
+ authenticate_repository (const common_options& co,
+ const dir_path* conf,
+ const optional<string>& cert_pem,
+ const certificate& cert,
+ const signature_manifest& sm,
+ const repository_location& rl)
+ {
+ tracer trace ("authenticate_repository");
+
+ if (conf != nullptr && conf->empty ())
+ conf = dir_exists (path (".bpkg")) ? &current_dir : nullptr;
+
+ assert (conf == nullptr || !conf->empty ());
+
+ path f;
+ auto_rmfile rm;
+
+ if (conf == nullptr)
+ {
+ // If we have no configuration, create the temporary certificate
+ // PEM file.
+ //
+ assert (cert_pem);
+
+ try
+ {
+ f = path::temp_path ("bpkg");
+
+ ofstream ofs;
+ ofs.exceptions (ofstream::badbit | ofstream::failbit);
+ ofs.open (f.string ());
+ rm = auto_rmfile (f);
+ ofs << *cert_pem;
+ }
+ catch (const ofstream::failure&)
+ {
+ fail << "unable to save certificate to temporary file " << f;
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to obtain temporary file: " << e.what ();
+ }
+ }
+ else
+ {
+ f = *conf / certs_dir / path (cert.fingerprint + ".pem");
+ }
+
+ const string& c (cert.name);
+ const string& r (rl.canonical_name ());
+ const size_t cn (c.size ());
+ const size_t rn (r.size ());
+
+ // Make sure the names are either equal or the certificate name is a
+ // prefix (at /-boundary) of the repository name.
+ //
+ if (!(r.compare (0, cn, c) == 0 &&
+ (rn == cn || (rn > cn && r[cn] == '/'))))
+ fail << "certificate name mismatch for repository " << r <<
+ info << "certificate name is " << c;
+
+ try
+ {
+ process pr (start_openssl (
+ co, "pkeyutl",
+ {
+ "-verifyrecover",
+ "-certin",
+ "-inkey",
+ f.string ().c_str ()
+ },
+ true,
+ true));
+
+ ifdstream is (pr.in_ofd);
+ is.exceptions (ifdstream::badbit);
+
+ try
+ {
+ ofdstream os (pr.out_fd);
+ os.exceptions (ofdstream::badbit);
+
+ for (const auto& c: sm.signature)
+ os.put (c); // Sets badbit on failure.
+
+ os.close ();
+
+ string s;
+ bool v (getline (is, s) && is.eof ());
+ is.close ();
+
+ if (pr.wait () && v)
+ {
+ if (s != sm.sha256sum)
+ fail << "packages manifest file signature mismatch for "
+ << rl.canonical_name ();
+
+ return; // All good.
+ }
+
+ // Fall through.
+ //
+ }
+ catch (const istream::failure&)
+ {
+ // Child input writing or output reading error.
+ //
+ is.close ();
+
+ // Child exit status doesn't matter. Just wait for the process
+ // completion and fall through.
+ //
+ pr.wait ();
+ }
+
+ error << "unable to authenticate repository " << rl.canonical_name ();
+
+ // Fall through.
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to authenticate repository "
+ << rl.canonical_name () << ": " << e.what ();
+
+ // Fall through.
+ }
+
+ throw failed ();
+ }
+
+ vector<char>
+ sign_repository (const common_options& co,
+ const string& sha256sum,
+ const string& key_name,
+ const string& cert_pem,
+ const dir_path& repository)
+ {
+ tracer trace ("sign_repository");
+
+ string r (repository.string () + dir_path::traits::directory_separator);
+
+ // No sense to calculate the fingerprint for the certificate being used
+ // just to check the expiration date.
+ //
+ shared_ptr<certificate> cert (parse_cert (co, "", cert_pem, r));
+
+ timestamp now (timestamp::clock::now ());
+
+ if (cert->end_date < now)
+ fail << "certificate for repository " << r << " has expired";
+
+ using days = chrono::duration<size_t, ratio<3600 * 24>>;
+
+ days left (chrono::duration_cast<days> (cert->end_date - now));
+ if (left < days (60))
+ warn << "certificate for repository " << r
+ << " expires in less than " << left.count () + 1 << " day(s)";
+
+ try
+ {
+ process pr (start_openssl (
+ co, "pkeyutl", {"-sign", "-inkey", key_name.c_str ()}, true, true));
+
+ ifdstream is (pr.in_ofd);
+ is.exceptions (ifdstream::badbit);
+
+ try
+ {
+ ofdstream os (pr.out_fd);
+ os.exceptions (ofdstream::badbit);
+ os << sha256sum;
+ os.close ();
+
+ // Additional parentheses required to make compiler to distinguish
+ // the variable definition from a function declaration.
+ //
+ vector<char> signature
+ ((istreambuf_iterator<char> (is)), istreambuf_iterator<char> ());
+
+ is.close ();
+
+ if (pr.wait ())
+ return signature;
+
+ // Fall through.
+ //
+ }
+ catch (const istream::failure&)
+ {
+ // Child input writing or output reading error.
+ //
+ is.close ();
+
+ // Child exit status doesn't matter. Just wait for the process
+ // completion and fall through.
+ //
+ pr.wait ();
+ }
+
+ error << "unable to sign repository " << r;
+
+ // Fall through.
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to sign repository " << r << ": " << e.what ();
+
+ // Fall through.
+ }
+
+ throw failed ();
+ }
+}
diff --git a/bpkg/buildfile b/bpkg/buildfile
index 5984b53..dec1aa7 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -9,6 +9,7 @@ import libs += libodb-sqlite%lib{odb-sqlite}
exe{bpkg}: \
{hxx cxx}{ archive } \
+{hxx cxx}{ auth } \
{hxx }{ bpkg-version } \
{ cxx}{ bpkg } {hxx ixx cxx}{ bpkg-options } \
{hxx cxx}{ cfg-add } {hxx ixx cxx}{ cfg-add-options } \
@@ -23,6 +24,7 @@ exe{bpkg}: \
{hxx }{ forward } \
{hxx cxx}{ help } {hxx ixx cxx}{ help-options } \
{hxx cxx}{ manifest-utility } \
+{hxx cxx}{ openssl } \
{hxx ixx cxx}{ package } \
{hxx ixx cxx}{ package-odb } file{ package.xml } \
{hxx cxx}{ pkg-build } {hxx ixx cxx}{ pkg-build-options } \
diff --git a/bpkg/cfg-create.cxx b/bpkg/cfg-create.cxx
index e50da8d..6d727fd 100644
--- a/bpkg/cfg-create.cxx
+++ b/bpkg/cfg-create.cxx
@@ -138,6 +138,10 @@ namespace bpkg
//
run_b (o, c, "configure(" + c.string () + "/)", true, vars); // Run quiet.
+ // Create .bpkg/.
+ //
+ mk (c / dir_path (".bpkg"));
+
// Create the database.
//
database db (open (c, trace, true));
diff --git a/bpkg/cfg-fetch.cxx b/bpkg/cfg-fetch.cxx
index d311a21..dfbcc98 100644
--- a/bpkg/cfg-fetch.cxx
+++ b/bpkg/cfg-fetch.cxx
@@ -8,6 +8,7 @@
#include <bpkg/manifest>
+#include <bpkg/auth>
#include <bpkg/fetch>
#include <bpkg/package>
#include <bpkg/package-odb>
@@ -20,7 +21,7 @@ using namespace butl;
namespace bpkg
{
static void
- cfg_fetch (const common_options& co,
+ cfg_fetch (const configuration_options& co,
transaction& t,
const shared_ptr<repository>& r,
const shared_ptr<repository>& root,
@@ -55,25 +56,50 @@ namespace bpkg
r->fetched = true; // Mark as being fetched.
- // Load the 'packages' file. We do this first so that we can get and
- // verify the checksum of the 'repositories' file which below.
+ // Load the 'repositories' file and use it to populate the prerequisite
+ // and complement repository sets.
//
- package_manifests pms (fetch_packages (co, rl, true));
+ pair<repository_manifests, string/*checksum*/> rmc (
+ fetch_repositories (co, rl, true));
- // Load the 'repositories' file and use it to populate the prerequisite and
- // complement repository sets.
- //
- repository_manifests rms;
+ repository_manifests& rms (rmc.first);
+
+ bool a (co.auth () != auth::none &&
+ (co.auth () == auth::all || rl.remote ()));
- try
+ shared_ptr<const certificate> cert;
+
+ if (a)
{
- rms = fetch_repositories (co, rl, pms.sha256sum, true);
+ cert = authenticate_certificate (
+ co, &co.directory (), rms.back ().certificate, rl);
+
+ a = !cert->dummy ();
}
- catch (const checksum_mismatch&)
- {
- fail << "repository files checksum mismatch for "
+
+ // Load the 'packages' file.
+ //
+ pair<package_manifests, string/*checksum*/> pmc (
+ fetch_packages (co, rl, true));
+
+ package_manifests& pms (pmc.first);
+
+ if (rmc.second != pms.sha256sum)
+ fail << "repositories manifest file checksum mismatch for "
<< rl.canonical_name () <<
info << "try again";
+
+ if (a)
+ {
+ signature_manifest sm (fetch_signature (co, rl, true));
+
+ if (sm.sha256sum != pmc.second)
+ fail << "packages manifest file checksum mismatch for "
+ << rl.canonical_name () <<
+ info << "try again";
+
+ assert (cert != nullptr);
+ authenticate_repository (co, &co.directory (), nullopt, *cert, sm, rl);
}
for (repository_manifest& rm: rms)
diff --git a/bpkg/common.cli b/bpkg/common.cli
index f15bb60..587d32d 100644
--- a/bpkg/common.cli
+++ b/bpkg/common.cli
@@ -2,7 +2,10 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+include <set>;
+
include <bpkg/types>;
+include <bpkg/options-types>;
"\section=1"
"\name=bpkg-common-options"
@@ -157,6 +160,60 @@ namespace bpkg
multiple tar options."
}
+ path --openssl = "openssl"
+ {
+ "<path>",
+ "The openssl program to be used for crypto operations. You can also
+ specify additional options that should be passed to the openssl
+ program with \cb{--openssl-option}. If the openssl program is not
+ explicitly specified, then \cb{bpkg} will use \cb{openssl} by default."
+ }
+
+ strings --openssl-option
+ {
+ "<opt>",
+ "Additional option to be passed to the openssl program. See
+ \cb{--openssl} for more information on the openssl program. Repeat this
+ option to specify multiple openssl options."
+ }
+
+ bpkg::auth --auth = bpkg::auth::remote
+ {
+ "<type>",
+ "Repository types be authenticated. Valid values for this option are
+ \cb{none}, \cb{remote}, \cb{all}. By default only remote repositories
+ are authenticated. You can request authentication of local repositories
+ by passing \cb{all} or disable authentication completely by passing
+ \cb{none}."
+ }
+
+ std::set<string> --trust
+ {
+ "<fingerprint>",
+ "Trust repository certificate with a SHA256 <fingerprint>. Such a
+ certificate is trusted automatically, without prompting the user for
+ a confirmation. Repeat this option to trust multiple certificates.
+
+ Note that by default \cb{openssl} prints a SHA1 fingerprint and to
+ obtain a SHA256 one you will need to pass the \cb{-sha256} option,
+ for example:
+
+ \
+ openssl x509 -sha256 -fingerprint -noout -in cert.pem
+ \
+ "
+ }
+
+ bool --trust-yes|-y
+ {
+ "Assume the answer to all authentication prompts is \cb{yes}."
+ }
+
+ bool --trust-no|-n
+ {
+ "Assume the answer to all authentication prompts is \cb{no}."
+ }
+
string --pager // String to allow empty value.
{
"<path>",
diff --git a/bpkg/database.cxx b/bpkg/database.cxx
index 54672d1..bffba02 100644
--- a/bpkg/database.cxx
+++ b/bpkg/database.cxx
@@ -21,7 +21,16 @@ namespace bpkg
{
tracer trace ("open");
- path f (d / path ("bpkg.sqlite3"));
+ // @@ Shouldn't we create database file in d / ".bpkg" directory ?
+ //
+ // @@ Yes, let's do it. Also perhaps downloaded packages as well?
+ // We might as well.
+ //
+ // @@ Don't think would be natural to keep package archives there as, the
+ // user should see which packages are downloaded without need to look
+ // into the "hidden" directory.
+ //
+ path f (d / path (".bpkg/bpkg.sqlite3"));
if (!create && !exists (f))
fail << d << " does not look like a bpkg configuration directory";
diff --git a/bpkg/fetch b/bpkg/fetch
index 2867243..2153df4 100644
--- a/bpkg/fetch
+++ b/bpkg/fetch
@@ -14,23 +14,26 @@
namespace bpkg
{
- class checksum_mismatch: public std::exception {};
-
- repository_manifests fetch_repositories (const dir_path&,
- bool ignore_unknown);
-
- // Verify the checksum and throw checksum_mismatch if it doesn't match.
- //
- repository_manifests fetch_repositories (const common_options&,
- const repository_location&,
- const string& sha256sum,
- bool ignore_unknown);
-
- package_manifests fetch_packages (const dir_path&,
- bool ignore_unknown);
- package_manifests fetch_packages (const common_options&,
- const repository_location&,
- bool ignore_unknown);
+ repository_manifests
+ fetch_repositories (const dir_path&, bool ignore_unknown);
+
+ pair<repository_manifests, string/*checksum*/>
+ fetch_repositories (const common_options&,
+ const repository_location&,
+ bool ignore_unknown);
+
+ package_manifests
+ fetch_packages (const dir_path&, bool ignore_unknown);
+
+ pair<package_manifests, string/*checksum*/>
+ fetch_packages (const common_options&,
+ const repository_location&,
+ bool ignore_unknown);
+
+ signature_manifest
+ fetch_signature (const common_options&,
+ const repository_location&,
+ bool ignore_unknown);
path
fetch_archive (const common_options&,
diff --git a/bpkg/fetch.cxx b/bpkg/fetch.cxx
index 9a5c74a..89c93e2 100644
--- a/bpkg/fetch.cxx
+++ b/bpkg/fetch.cxx
@@ -524,16 +524,13 @@ namespace bpkg
return r;
}
- // If sha256sum is empty, then don't verify.
- //
template <typename M>
- static M
+ static pair<M, string/*checksum*/>
fetch_manifest (const common_options& o,
protocol proto,
const string& host,
uint16_t port,
const path& f,
- const string& sha256sum,
bool ignore_unknown)
{
string url (to_url (proto, host, port, f));
@@ -544,35 +541,24 @@ namespace bpkg
ifdstream is (pr.in_ofd);
is.exceptions (ifdstream::badbit | ifdstream::failbit);
- M m;
- if (sha256sum.empty ())
- {
- manifest_parser mp (is, url);
- m = M (mp, ignore_unknown);
- is.close ();
- }
- else
- {
- // Unfortunately we cannot rewind STDOUT as we do below for files.
- // There doesn't seem to be anything better than reading the entire
- // file into memory and then streaming it twice, once to calculate
- // the checksum and the second time to actually parse.
- //
- stringstream ss;
- ss << is.rdbuf ();
- is.close ();
-
- if (sha256sum != sha256 (o, ss))
- throw checksum_mismatch ();
-
- ss.clear (); ss.seekg (0); // Rewind.
-
- manifest_parser mp (ss, url);
- m = M (mp, ignore_unknown);
- }
+ // Unfortunately we cannot rewind STDOUT as we do below for files. There
+ // doesn't seem to be anything better than reading the entire file into
+ // memory and then streaming it twice, once to calculate the checksum
+ // and the second time to actually parse.
+ //
+ stringstream ss;
+ ss << is.rdbuf ();
+ is.close ();
+
+ string sha256sum (sha256 (o, ss));
+
+ ss.clear (); ss.seekg (0); // Rewind.
+
+ manifest_parser mp (ss, url);
+ M m (mp, ignore_unknown);
if (pr.wait ())
- return m;
+ return make_pair (move (m), move (sha256sum));
// Child existed with an error, fall through.
}
@@ -645,13 +631,12 @@ namespace bpkg
return r;
}
- // If sha256sum is empty, then don't verify.
+ // If o is nullptr, then don't calculate the checksum.
//
template <typename M>
- static M
+ static pair<M, string/*checksum*/>
fetch_manifest (const common_options* o,
const path& f,
- const string& sha256sum,
bool ignore_unknown)
{
if (!exists (f))
@@ -663,18 +648,15 @@ namespace bpkg
ifs.exceptions (ofstream::badbit | ofstream::failbit);
ifs.open (f.string ());
- if (!sha256sum.empty ())
+ string sha256sum;
+ if (o != nullptr)
{
- assert (o != nullptr);
-
- if (sha256sum != sha256 (*o, ifs))
- throw checksum_mismatch ();
-
+ sha256sum = sha256 (*o, ifs);
ifs.seekg (0); // Rewind the file stream.
}
manifest_parser mp (ifs, f.string ());
- return M (mp, ignore_unknown);
+ return make_pair (M (mp, ignore_unknown), move (sha256sum));
}
catch (const manifest_parsing& e)
{
@@ -694,16 +676,12 @@ namespace bpkg
fetch_repositories (const dir_path& d, bool iu)
{
return fetch_manifest<repository_manifests> (
- nullptr,
- d / repositories,
- "", // No checksum verification.
- iu);
+ nullptr, d / repositories, iu).first;
}
- repository_manifests
+ pair<repository_manifests, string/*checksum*/>
fetch_repositories (const common_options& o,
const repository_location& rl,
- const string& sha256sum,
bool iu)
{
assert (rl.remote () || rl.absolute ());
@@ -712,8 +690,8 @@ namespace bpkg
return rl.remote ()
? fetch_manifest<repository_manifests> (
- o, rl.proto (), rl.host (), rl.port (), f, sha256sum, iu)
- : fetch_manifest<repository_manifests> (&o, f, sha256sum, iu);
+ o, rl.proto (), rl.host (), rl.port (), f, iu)
+ : fetch_manifest<repository_manifests> (&o, f, iu);
}
static const path packages ("packages");
@@ -721,10 +699,10 @@ namespace bpkg
package_manifests
fetch_packages (const dir_path& d, bool iu)
{
- return fetch_manifest<package_manifests> (nullptr, d / packages, "", iu);
+ return fetch_manifest<package_manifests> (nullptr, d / packages, iu).first;
}
- package_manifests
+ pair<package_manifests, string/*checksum*/>
fetch_packages (const common_options& o,
const repository_location& rl,
bool iu)
@@ -735,8 +713,25 @@ namespace bpkg
return rl.remote ()
? fetch_manifest<package_manifests> (
- o, rl.proto (), rl.host (), rl.port (), f, "", iu)
- : fetch_manifest<package_manifests> (&o, f, "", iu);
+ o, rl.proto (), rl.host (), rl.port (), f, iu)
+ : fetch_manifest<package_manifests> (&o, f, iu);
+ }
+
+ static const path signature ("signature");
+
+ signature_manifest
+ fetch_signature (const common_options& o,
+ const repository_location& rl,
+ bool iu)
+ {
+ assert (rl.remote () || rl.absolute ());
+
+ path f (rl.path () / signature);
+
+ return rl.remote ()
+ ? fetch_manifest<signature_manifest> (
+ o, rl.proto (), rl.host (), rl.port (), f, iu).first
+ : fetch_manifest<signature_manifest> (nullptr, f, iu).first;
}
path
diff --git a/bpkg/openssl b/bpkg/openssl
new file mode 100644
index 0000000..c2f8712
--- /dev/null
+++ b/bpkg/openssl
@@ -0,0 +1,31 @@
+// file : bpkg/openssl -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_OPENSSL
+#define BPKG_OPENSSL
+
+#include <butl/process>
+
+#include <bpkg/types>
+#include <bpkg/utility>
+
+#include <bpkg/common-options>
+
+namespace bpkg
+{
+ // Start the openssl process. Parameters in, out, err flags if the caller
+ // wish to write to, or read from the process STDIN, STDOUT, STDERR streams.
+ // If out and err are both true, then STDERR is redirected to STDOUT, and
+ // they both can be read from in_ofd descriptor.
+ //
+ butl::process
+ start_openssl (const common_options&,
+ const char* command,
+ const cstrings& options,
+ bool in = false,
+ bool out = false,
+ bool err = false);
+}
+
+#endif // BPKG_OPENSSL
diff --git a/bpkg/openssl.cxx b/bpkg/openssl.cxx
new file mode 100644
index 0000000..67500b6
--- /dev/null
+++ b/bpkg/openssl.cxx
@@ -0,0 +1,59 @@
+// file : bpkg/openssl.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/openssl>
+
+#include <butl/process>
+#include <butl/fdstream>
+
+#include <bpkg/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ process
+ start_openssl (const common_options& co,
+ const char* command,
+ const cstrings& options,
+ bool in,
+ bool out,
+ bool err)
+ {
+ cstrings args {co.openssl ().string ().c_str (), command};
+
+ // Add extra options. Normally the order of options is not important
+ // (unless they override each other). However, openssl 1.0.1 seems to have
+ // bugs in that department (that were apparently fixed in 1.0.2). To work
+ // around these bugs we pass user-supplied options first.
+ //
+ for (const string& o: co.openssl_option ())
+ args.push_back (o.c_str ());
+
+ args.insert (args.end (), options.begin (), options.end ());
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+
+ try
+ {
+ // If the caller is interested in reading STDOUT and STDERR, then
+ // redirect STDERR to STDOUT, so both can be read from the same stream.
+ //
+ return process (
+ args.data (), in ? -1 : 0, out ? -1 : 1, err ? (out ? 1 : -1): 2);
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e.what ();
+
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
+ }
+}
diff --git a/bpkg/options-types b/bpkg/options-types
new file mode 100644
index 0000000..88e6b67
--- /dev/null
+++ b/bpkg/options-types
@@ -0,0 +1,18 @@
+// file : bpkg/options-types -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_OPTIONS_TYPES
+#define BPKG_OPTIONS_TYPES
+
+namespace bpkg
+{
+ enum class auth
+ {
+ none,
+ remote,
+ all
+ };
+}
+
+#endif // BPKG_OPTIONS_TYPES
diff --git a/bpkg/package b/bpkg/package
index 779f0e2..2bfc575 100644
--- a/bpkg/package
+++ b/bpkg/package
@@ -7,14 +7,19 @@
#include <map>
#include <set>
+#include <ratio>
+#include <chrono>
+#include <type_traits> // static_assert
#include <odb/core.hxx>
#include <odb/nested-container.hxx>
+#include <butl/timestamp>
+
#include <bpkg/types>
#include <bpkg/utility>
-#pragma db model version(2, 2, closed)
+#pragma db model version(3, 3, open)
namespace bpkg
{
@@ -50,6 +55,29 @@ namespace bpkg
to((?) ? (?)->string () : bpkg::optional_string ()) \
from((?) ? bpkg::dir_path (*(?)) : bpkg::optional_dir_path ())
+ // timestamp
+ //
+ using butl::timestamp;
+ using butl::timestamp_unknown;
+
+ // Ensure that timestamp can be represented in nonoseconds without loss of
+ // accuracy, so the following ODB mapping is adequate.
+ //
+ static_assert (
+ std::ratio_greater_equal<timestamp::period,
+ std::chrono::nanoseconds::period>::value,
+ "The following timestamp ODB mapping is invalid");
+
+ // As pointed out in butl/timestamp we will overflow in year 2262, but by
+ // that time some larger basic type will be available for mapping.
+ //
+ #pragma db map type(timestamp) as(uint64_t) \
+ to(std::chrono::duration_cast<std::chrono::nanoseconds> ( \
+ (?).time_since_epoch ()).count ()) \
+ from(butl::timestamp ( \
+ std::chrono::duration_cast<butl::timestamp::duration> ( \
+ std::chrono::nanoseconds (?))))
+
// An image type that is used to map version to the database since
// there is no way to modify individual components directly. We have
// to define it before including <bpkg/manifest> since some value
@@ -176,7 +204,7 @@ namespace bpkg
// repository
//
- #pragma db object pointer(std::shared_ptr) session
+ #pragma db object pointer(shared_ptr) session
class repository
{
public:
@@ -465,6 +493,90 @@ namespace bpkg
selected_package () = default;
};
+
+ // certificate
+ //
+ // Information extracted from a repository X.509 certificate. The actual
+ // certificate is stored on disk as .bpkg/certs/<fingerprint>.pem (we have
+ // to store it as a file because that's the only way to pass it to openssl).
+ //
+ // If a repository is not authenticated (has no certificate/signature,
+ // called unauth from now on), then we ask for the user's confirmation and
+ // create a dummy certificate in order not to ask for the same confirmation
+ // (for this repository) on next fetch. The problem is, there could be
+ // multiple sections in such a repository and it would be annoying to
+ // confirm all of them. So what we are going to do is create a dummy
+ // certificate not for this specific repository location but for a
+ // repository location only up to the version, so the name member will
+ // contain the name prefix rather than the full name (just like a normal
+ // certificate would). The fingerprint member for such a dummy certificate
+ // contains the SHA256 checksum of this name. Members other then name and
+ // fingerprint are meaningless for the dummy certificate.
+ //
+ #pragma db object pointer(shared_ptr) session
+ class certificate
+ {
+ public:
+ string fingerprint; // Object id (note: SHA256 fingerprint).
+
+ string name; // CN component of Subject.
+ string organization; // O component of Subject.
+ string email; // email: in Subject Alternative Name.
+
+ timestamp start_date; // notBefore (UTC)
+ timestamp end_date; // notAfter (UTC)
+
+ bool
+ dummy () const {return start_date == timestamp_unknown;}
+
+ bool
+ expired () const
+ {
+ assert (!dummy ());
+ return timestamp::clock::now () > end_date;
+ }
+
+ public:
+ certificate (string f,
+ string n,
+ string o,
+ string e,
+ timestamp sd,
+ timestamp ed)
+ : fingerprint (move (f)),
+ name (move (n)),
+ organization (move (o)),
+ email (move (e)),
+ start_date (move (sd)),
+ end_date (move (ed))
+ {
+ }
+
+ // Create dummy certificate.
+ //
+ certificate (string f, string n)
+ : fingerprint (move (f)),
+ name (move (n)),
+ start_date (timestamp_unknown),
+ end_date (timestamp_unknown)
+ {
+ }
+
+ // Database mapping.
+ //
+ #pragma db member(fingerprint) id
+
+ private:
+ friend class odb::access;
+ certificate () = default;
+ };
+
+ // Note: prints all the certificate information on one line so mostly
+ // useful for tracing.
+ //
+ ostream&
+ operator<< (ostream&, const certificate&);
+
// Return a list of packages that depend on this package along with
// their constraints.
//
diff --git a/bpkg/package.cxx b/bpkg/package.cxx
index a72ab21..2cf96ff 100644
--- a/bpkg/package.cxx
+++ b/bpkg/package.cxx
@@ -129,4 +129,20 @@ namespace bpkg
else if (s == "configured") return package_state::configured;
else throw invalid_argument ("invalid package state '" + s + "'");
}
+
+ // certificate
+ //
+ ostream&
+ operator<< (ostream& os, const certificate& c)
+ {
+ using butl::operator<<;
+
+ if (c.dummy ())
+ os << c.name << " (dummy)";
+ else
+ os << c.name << ", \"" << c.organization << "\" <" << c.email << ">, "
+ << c.start_date << " - " << c.end_date << ", " << c.fingerprint;
+
+ return os;
+ }
}
diff --git a/bpkg/package.xml b/bpkg/package.xml
index 0e0872d..900794c 100644
--- a/bpkg/package.xml
+++ b/bpkg/package.xml
@@ -1,5 +1,5 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
- <model version="2">
+ <model version="3">
<table name="repository" kind="object">
<column name="name" type="TEXT" null="true"/>
<column name="location" type="TEXT" null="true"/>
@@ -231,5 +231,16 @@
</references>
</foreign-key>
</table>
+ <table name="certificate" kind="object">
+ <column name="fingerprint" type="TEXT" null="true"/>
+ <column name="name" type="TEXT" null="true"/>
+ <column name="organization" type="TEXT" null="true"/>
+ <column name="email" type="TEXT" null="true"/>
+ <column name="start_date" type="INTEGER" null="true"/>
+ <column name="end_date" type="INTEGER" null="true"/>
+ <primary-key>
+ <column name="fingerprint"/>
+ </primary-key>
+ </table>
</model>
</changelog>
diff --git a/bpkg/rep-create.cli b/bpkg/rep-create.cli
index fc11a0c..a48922e 100644
--- a/bpkg/rep-create.cli
+++ b/bpkg/rep-create.cli
@@ -20,9 +20,12 @@ namespace bpkg
\h|DESCRIPTION|
The \cb{rep-create} command regenerates the \cb{packages} manifest file
- based on the files present in the repository directory. If <dir> is not
- specified, then the current working directory is used as the repository
- root."
+ based on the files present in the repository directory. If the
+ \cb{repositories} manifest file contains a certificate, then the
+ \cb{signature} manifest file is regenerated as well. In this case the
+ \cb{--key} option must be used to specify the certificate's private
+ key. If <dir> is not specified, then the current working directory is
+ used as the repository root."
}
class rep_create_options: common_options
@@ -33,5 +36,13 @@ namespace bpkg
{
"Ignore unknown manifest entries."
}
+
+ string --key
+ {
+ "<name>",
+ "Private key to use to sign the repository. In most cases <name> will
+ be a path to the key file but it can also be a key id when a custom
+ \cb{openssl} cryptographic engine is used."
+ }
};
}
diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx
index 9c6f275..2ab52fb 100644
--- a/bpkg/rep-create.cxx
+++ b/bpkg/rep-create.cxx
@@ -13,6 +13,7 @@
#include <bpkg/manifest>
#include <bpkg/manifest-serializer>
+#include <bpkg/auth>
#include <bpkg/fetch>
#include <bpkg/archive>
#include <bpkg/checksum>
@@ -52,6 +53,7 @@ namespace bpkg
static const path repositories ("repositories");
static const path packages ("packages");
+ static const path signature ("signature");
static void
collect (const rep_create_options& o,
@@ -91,7 +93,7 @@ namespace bpkg
//
if (d == root)
{
- if (p == repositories || p == packages)
+ if (p == repositories || p == packages || p == signature)
continue;
}
@@ -208,18 +210,52 @@ namespace bpkg
manifests.emplace_back (move (m));
}
- // Serialize.
+ // Serialize packages manifest, optionally generate the signature manifest.
//
path p (d / packages);
try
{
- ofstream ofs;
- ofs.exceptions (ofstream::badbit | ofstream::failbit);
- ofs.open (p.string ());
+ {
+ ofstream ofs;
+ ofs.exceptions (ofstream::badbit | ofstream::failbit);
+ ofs.open (p.string ());
+
+ manifest_serializer s (ofs, p.string ());
+ manifests.serialize (s);
+ }
+
+ const optional<string>& cert (rms.back ().certificate);
+ if (cert)
+ {
+ const string& key (o.key ());
+ if (key.empty ())
+ fail << "--key option required" <<
+ info << "repository manifest contains a certificate" <<
+ info << "run 'bpkg help rep-create' for more information";
+
+ signature_manifest m;
+ m.sha256sum = sha256 (o, p);
+ m.signature = sign_repository (o, m.sha256sum, key, *cert, d);
- manifest_serializer s (ofs, p.string ());
- manifests.serialize (s);
+ p = path (d / signature);
+
+ ofstream ofs;
+ ofs.exceptions (ofstream::badbit | ofstream::failbit);
+ ofs.open (p.string ());
+
+ manifest_serializer s (ofs, p.string ());
+ m.serialize (s);
+ }
+ else
+ {
+ if (o.key_specified ())
+ warn << "--key option ignored" <<
+ info << "repository manifest contains no certificate" <<
+ info << "run 'bpkg help rep-create' for more information";
+
+ try_rmfile (path (d / signature), true);
+ }
}
catch (const manifest_serialization& e)
{
diff --git a/bpkg/rep-info.cli b/bpkg/rep-info.cli
index 7554620..5fa5d3d 100644
--- a/bpkg/rep-info.cli
+++ b/bpkg/rep-info.cli
@@ -24,7 +24,16 @@ namespace bpkg
first line followed by the list of complement and prerequisite
repositories and the list of available packages. This default behavior,
however, can be altered in various ways using options listed below. Note
- that the information is written to \cb{STDOUT}, not \cb{STDERR}."
+ that the information is written to \cb{STDOUT}, not \cb{STDERR}.
+
+ If the current working directory contains a \cb{bpkg} configuration, then
+ \cb{rep-info} will use its certificate database for the repository
+ authentication. That is, it will trust the repository's certificate if it
+ is already trusted by the configuration. Otherwise it will add the
+ certificate to the configuration if you confirm it is trusted. You can
+ specify an alternative configuration directory with the
+ \cb{--directory|-d} option. To disable using the configuration in the
+ current working directory pass this option with an empty path."
}
class rep_info_options: common_options
@@ -53,5 +62,11 @@ namespace bpkg
\cb{--packages|-p} or \cb{--repositories|-r} to only dump one of the
manifests."
}
+
+ string --directory|-d // String to allow empty value.
+ {
+ "<dir>",
+ "Use configuration in <dir> for the trusted certificate database."
+ }
};
}
diff --git a/bpkg/rep-info.cxx b/bpkg/rep-info.cxx
index d2157af..9cad4fd 100644
--- a/bpkg/rep-info.cxx
+++ b/bpkg/rep-info.cxx
@@ -9,7 +9,9 @@
#include <bpkg/manifest>
#include <bpkg/manifest-serializer>
+#include <bpkg/auth>
#include <bpkg/fetch>
+#include <bpkg/package>
#include <bpkg/diagnostics>
#include <bpkg/manifest-utility>
@@ -30,21 +32,64 @@ namespace bpkg
repository_location rl (parse_location (args.next ()));
// Fetch everything we will need before printing anything. Ignore
- // unknown manifest entries unless we are dumping them.
+ // unknown manifest entries unless we are dumping them. First fetch
+ // the repositories list and authenticate the base's certificate.
//
- package_manifests pms (fetch_packages (o, rl, !o.manifest ()));
+ pair<repository_manifests, string/*checksum*/> rmc (
+ fetch_repositories (o, rl, !o.manifest ()));
- repository_manifests rms;
+ repository_manifests& rms (rmc.first);
- try
+ bool a (o.auth () != auth::none &&
+ (o.auth () == auth::all || rl.remote ()));
+
+ const optional<string> cert_pem (rms.back ().certificate);
+ shared_ptr<const certificate> cert;
+
+ if (a)
{
- rms = fetch_repositories (o, rl, pms.sha256sum, !o.manifest ());
+ dir_path d (o.directory ());
+ cert = authenticate_certificate (
+ o,
+ o.directory_specified () && d.empty () ? nullptr : &d,
+ cert_pem,
+ rl);
+
+ a = !cert->dummy ();
}
- catch (const checksum_mismatch&)
- {
- fail << "repository files checksum mismatch for "
+
+ // Now fetch the packages list and make sure it matches the repositories
+ // we just fetched.
+ //
+ pair<package_manifests, string/*checksum*/> pmc (
+ fetch_packages (o, rl, !o.manifest ()));
+
+ package_manifests& pms (pmc.first);
+
+ if (rmc.second != pms.sha256sum)
+ fail << "repositories manifest file checksum mismatch for "
<< rl.canonical_name () <<
info << "try again";
+
+ if (a)
+ {
+ signature_manifest sm (fetch_signature (o, rl, true));
+
+ if (sm.sha256sum != pmc.second)
+ fail << "packages manifest file checksum mismatch for "
+ << rl.canonical_name () <<
+ info << "try again";
+
+ dir_path d (o.directory ());
+ assert (cert != nullptr);
+
+ authenticate_repository (
+ o,
+ o.directory_specified () && d.empty () ? nullptr : &d,
+ cert_pem,
+ *cert,
+ sm,
+ rl);
}
// Now print.
diff --git a/bpkg/types-parsers b/bpkg/types-parsers
index 41046a1..41fab10 100644
--- a/bpkg/types-parsers
+++ b/bpkg/types-parsers
@@ -9,6 +9,7 @@
#define BPKG_TYPES_PARSERS
#include <bpkg/types>
+#include <bpkg/options-types>
namespace bpkg
{
@@ -32,6 +33,13 @@ namespace bpkg
static void
parse (dir_path&, bool&, scanner&);
};
+
+ template <>
+ struct parser<auth>
+ {
+ static void
+ parse (auth&, bool&, scanner&);
+ };
}
}
diff --git a/bpkg/types-parsers.cxx b/bpkg/types-parsers.cxx
index 9e97ccd..6a61a3d 100644
--- a/bpkg/types-parsers.cxx
+++ b/bpkg/types-parsers.cxx
@@ -47,5 +47,25 @@ namespace bpkg
xs = true;
parse_path (x, s);
}
+
+ void parser<auth>::
+ parse (auth& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const string v (s.next ());
+ if (v == "none")
+ x = auth::none;
+ else if (v == "remote")
+ x = auth::remote;
+ else if (v == "all")
+ x = auth::all;
+ else
+ throw invalid_value (o, v);
+ }
}
}
diff --git a/bpkg/utility b/bpkg/utility
index 009ef76..7bb6111 100644
--- a/bpkg/utility
+++ b/bpkg/utility
@@ -35,7 +35,7 @@ namespace bpkg
using butl::reverse_iterate;
// Y/N prompt. The def argument, if specified, should be either 'y'
- // or 'no'. It is used as the default answer, in case the user just
+ // or 'n'. It is used as the default answer, in case the user just
// hits enter. Issue diagnostics and throw failed if no answer could
// be extracted from STDOUT (e.g., because it was closed).
//
diff --git a/tests/cert.sh b/tests/cert.sh
new file mode 100755
index 0000000..156f588
--- /dev/null
+++ b/tests/cert.sh
@@ -0,0 +1,46 @@
+#! /bin/sh
+
+# Normally, you don't need to regenerate the private key.
+#
+# openssl genrsa 4096 > key.pem
+
+# Copy default-cert.pem content to the certificate value of the following
+# manifest files:
+# pkg/1/build2.org/auth/mismatched/signature/repositories
+# pkg/1/build2.org/auth/mismatched/sha256sum/repositories
+# pkg/1/build2.org/auth/signed/repositories
+# pkg/1/build2.org/common/hello/repositories
+#
+openssl req -x509 -new -key key.pem -days 365 -config default-openssl.cnf > \
+ default-cert.pem
+
+# Copy mismatch-cert.pem content to the certificate value of
+# pkg/1/build2.org/auth/mismatched/name/repositories manifest file.
+#
+openssl req -x509 -new -key key.pem -days 365 -config mismatch-openssl.cnf > \
+ mismatch-cert.pem
+
+# Copy noemail-cert.pem content to the certificate value of
+# pkg/1/faulty/auth/noemail/repositories manifest file.
+#
+openssl req -x509 -new -key key.pem -days 365 -config noemail-openssl.cnf > \
+ noemail-cert.pem
+
+# Normally, you have no reason to regenerate expired-cert.pem, as need to keep
+# it expired for the testing purposes. But if you do, copy expired-cert.pem
+# content to the certificate value of the following manifest files:
+# pkg/1/build2.org/auth/expired/repositories
+# pkg/1/faulty/auth/expired/repositories
+#
+# To regenerate the packages and signature manifest files run:
+#
+# ../bpkg/bpkg rep-create pkg/1/build2.org/auth/expired --key key.pem
+#
+# We cannot do it in test.sh since the certificate has expired. This is also
+# the reason why we store these auto-generated manifests in git.
+#
+# Will have to wait 1 day until the certificate expires. Until then test.sh
+# will be failing.
+#
+# openssl req -x509 -new -key key.pem -days 1 -config default-openssl.cnf > \
+# expired-cert.pem
diff --git a/tests/default-openssl.cnf b/tests/default-openssl.cnf
new file mode 100644
index 0000000..c14ec06
--- /dev/null
+++ b/tests/default-openssl.cnf
@@ -0,0 +1,22 @@
+repository = build2.org
+company = Code Synthesis
+email = info@build2.org
+
+
+[ req ]
+
+distinguished_name = req_distinguished_name
+x509_extensions = v3_req
+prompt = no
+utf8 = yes
+
+[ req_distinguished_name ]
+
+O = $company
+CN = name:$repository
+
+[ v3_req ]
+
+keyUsage = critical,digitalSignature
+extendedKeyUsage = critical,codeSigning
+subjectAltName = email:$email
diff --git a/tests/key.pem b/tests/key.pem
new file mode 100644
index 0000000..6615a2d
--- /dev/null
+++ b/tests/key.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+IfdyjGMu
+34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNPPR58SFNw
+wd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH4O3aL2HB
+0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXNCJ8uE8yU
+cuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2HY478bHLT
+2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0HlQIAGOYZz
+xbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThubomWaImT
+xTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6aI+yGPsWP
+AhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+u7jGNUzp
+9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqaDZlnVvfw
+ozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU4JECAwEA
+AQKCAgAz75YlpJq/l5i4XrUusSlYZxRyZNWpjwiFGspox8QwenmnXdF+XmLUwjV5
+UFuMcLwWrzHbzrUdV+5/hHCJHP8aQY3DcOJdlIchqZgdhf61i2yFG9LVJUzueGxP
+mtAkc+K8ZcaSzW2BxLSjIyvr9QS+T7cHVsCQtNefvdRMU6P9A1zgDT0g+JnSt8z0
+gPnpvMpVWM67MDfn029tHHf3V+5Bs4A51sXeQpPWENnBJRkW5/zf1J6W3XQR5k3q
+KGW1Yk4aATGIfN5BPwax+h70jInlALKOnprJFrnjvn47E1NnT0ceVPF6u2iaIwqX
+cCEu1e3flGrrF7Ot9vilG5XL/k8q24LlPrwrjTxgNxoFtSQTHKXWir4hahPEWUJo
+lbPxUaVXq0S3OsWxPbj5pAOxP5aZ/vATHsa68tQuErDif4zXhoSU1T06Tcb9pPaP
+duVa0QXAy9uiK+NnFbisK7/rC+HTXz7ZdQsAm1XrwkPiLaLbaK30BS0yUNZRjUBc
+gQYVShqD6CG9wo9D4HCAPulfcnJ5NqMjTOWDdoY6dAoESD/LYZntjJz5BB0hC9bH
+iDIt3yrWjE8khksKvMRyHbzpOq8M6ngZWGTOwMlQ4SEbJaqYfRSK4JN7ePgIMbsE
+xAIC8TAPzJNj8Fas8WG2kLTxgMdGe/LcrTL43aR+kkza6MYaVQKCAQEA7cMWLm2f
+8vHr1YdBu5gaWIE1TGbZD50akRPjilBBawAXc1ho9B6HdK+l0YYXFe5jRdOWXEn0
+fOUOWR68rkW813kPyWsEGeUfaGUABIomCJIbzV21BvaNgeJIxQIhAr94lX4iVvOh
+KyCkZa7NM6lYR77SrLtZgOtZjymnAN/ox78t+AyasRD2ToxoTMfgCPvbPqsCcd7i
+SamIZrcGCSiz5eZaIyTgV2z8jcobSO2316LcuYCVUe47Y0Hcdmzqq7pJwym7bsiH
+3nypIqVtiSvqXirst56KyAvFAfbhZFy0vx0q4PwkovLZXcdQvmKBkn5wQRIhHlFT
+AWLwnxvBW2K+2wKCAQEA64M0tlMhXOW4RxwnJJVuqQJfeRj7Eg8mxJeOSaQIKW/2
+D9G1dqbxzcfzo6G+EFLB8e9qsap4KY1FWHjiqh5P4uheET9AN41YnBw2S8y5eNQn
+uJdWU5B39vLfJJ/3A/IiiQ8ok+t2kyZyNpoeu90FKlNL/4mWFhRTCvG+G9ZHFkVJ
+eDV7lJrErTpTX0XwHahtrOxrYMu6vj+GYFnoeMcXLsy4fb7d7CZ1qqyyEjwDkdaS
+UEtb0MVatWEoBb2eCXDJnWmwwncSfgNWEicV+FNV9NLUrF4SOmeNkF0/GVMMRxZe
+lBODoY99UrP6vtukkRJ8B+ObcQu0VwBoHP97zA0sAwKCAQEA3hmzCsPU+nkKuHiU
+6sx3L93jD++zLGaxfnx5vaaitXVsco9P21r5ZgL+fu8neZEqovu4JZXgERtXf+C6
+mIEEmrO4D4ssVZQ29qoCjjsOtEroNbsnc820+nOLTp1igNKjreIZ6efD0im36FSV
+04CJutuCqWCJh2hSTO2315VSOeVY57G+f1E9BLscYz0DBuFeOPg+XGi8MdtwU6L8
+vGmNQ2EfKl00Q/bHJRx6yHFnnsPANHjtA4/rYhkP/IBZ5vkqFmlRcXdZlHgW9wpZ
+fB3N3RFz90LemWiFLASXH82J+k0g1PI0txLsAjJCNtaF+3apHd/1mACX7V+jLr4a
+rWdcqQKCAQA/raI7Cg8jRxmkYIBU6/KX0ARwgcihAWfCEBhw4OE0Ewm9DT67bjhD
+I6VEwR7a2wWFnO6ES0tiMMhnO4hN5xRCjgGhODDs1FL5Iserhxc/DF4RExpMTeuh
+4dlj1by8XGPqGycll6zf7K1FBjb5JLGY2BzcYx8pqSUq+GImtLFT3DgZQJzBeXdk
+Pzj7sbVSz4+YCfSen3dGqXZz5ttsjKhAByp7FXh7uYpnM3qSY5ezfwjgnP+eWz5D
+fJmNs89fThe1cDZS3k8ps006a0cNdbrwD3Fhia30DLvCJPRCrKnNLscz0nLs5BdG
+nVNW95xvfUf7+ha1YizIuVttO8cFTSpxAoIBAET0kO0jKwSYzBBJBMWHlAmYfaiW
+WUUYAYpk2jHaK8L4QSNZOvBIEovMCdNN9+D8yQ09HGMxny5+MkSF9CaRRdC1MFfg
+bZVlmC+UlzeuMEHalrYVWmIQ29KsF7vo9dKBz1BHMQ77D4lzaV1fZ9EPg+XbbNG2
+6N/rC8ubuC4CjnqMCO0tFM+Gq6ixzDmBkzEWYStEwMQVIkfJ/BxVFHyH8DsRDmvO
+1u3lZ7Ks0HyiSOe45q/E6PnNV+oV6f1EeZESZysuD96fHm8XMC6iBwK0AQ0hUZ4e
+cVzIsHBEMzLJx6On7xQYR7FNmdW+8fJmMXyniCUS9c26upTNwWVDX+U4Y4M=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/mismatch-openssl.cnf b/tests/mismatch-openssl.cnf
new file mode 100644
index 0000000..f4fe4d7
--- /dev/null
+++ b/tests/mismatch-openssl.cnf
@@ -0,0 +1,22 @@
+repository = build2.org/auth/mismatched/name/x
+company = Code Synthesis
+email = info@build2.org
+
+
+[ req ]
+
+distinguished_name = req_distinguished_name
+x509_extensions = v3_req
+prompt = no
+utf8 = yes
+
+[ req_distinguished_name ]
+
+O = $company
+CN = name:$repository
+
+[ v3_req ]
+
+keyUsage = critical,digitalSignature
+extendedKeyUsage = critical,codeSigning
+subjectAltName = email:$email
diff --git a/tests/noemail-openssl.cnf b/tests/noemail-openssl.cnf
new file mode 100644
index 0000000..4fc8618
--- /dev/null
+++ b/tests/noemail-openssl.cnf
@@ -0,0 +1,20 @@
+repository = build2.org
+company = Code Synthesis
+
+
+[ req ]
+
+distinguished_name = req_distinguished_name
+x509_extensions = v3_req
+prompt = no
+utf8 = yes
+
+[ req_distinguished_name ]
+
+O = $company
+CN = name:$repository
+
+[ v3_req ]
+
+keyUsage = critical,digitalSignature
+extendedKeyUsage = critical,codeSigning
diff --git a/tests/pkg/.gitignore b/tests/pkg/.gitignore
new file mode 100644
index 0000000..cd1ec36
--- /dev/null
+++ b/tests/pkg/.gitignore
@@ -0,0 +1,2 @@
+packages
+signature
diff --git a/tests/repository/1/fetch/t1/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/create-expired/libfoo-1.0.0.tar.gz
index 32e5a3c..32e5a3c 120000
--- a/tests/repository/1/fetch/t1/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/auth/create-expired/libfoo-1.0.0.tar.gz
diff --git a/tests/pkg/1/build2.org/auth/create-expired/repositories b/tests/pkg/1/build2.org/auth/create-expired/repositories
new file mode 100644
index 0000000..8370fa4
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/create-expired/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAPIoh0763iYdMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoMDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDDA9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNTAxMTA1MDMyWhcNMTYwNTAyMTA1MDMyWjAzMRcwFQYDVQQKDA5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAwwPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAAb+Yxvqi4sOZ+Ti8wcxkxqFrIs32M3XgJIeZYJn9JZbtjuBx0JFZWnL/tz4M
+VTiM2nrUc4qIIpTqEt2If1NcdH0Cjvf6vysLvIOoB4q09B+aW4F+19moixYodoDp
+qq1Ki1MZrBMx+KUICagqRSklPyxaZ2rEFv2TY0pg8/QRFRWGCFgdWJleX6VsqIXC
+KDK6MyHfR1gsDnZZfgr7HJCq1SH3loqlchLBvAF6K/c1LdQyJFYoP4xAt6yaQWzv
+wEqqhhZ7tLpjrjK0K2pqsPoupNcNhE31k+6ZdM5Z5oHT844qKIVNJIdlvFwnQ3ed
+grP6gBCLVY7h7GJ0x2ZG/2hYt4My8RqLxFYHaHAzQsmXFATIzT1Yqz1nw+8V3l5P
+m+rXQNVAw7rgpl3T7klbHVfkg5rPa0kF66PkKq5ax46NS6KVj9sNOqkcXL7huQSG
+BnSLTkaLwwrSZWl+DPXeY8TgUGkkEMY6RvYdqoOGZiZSqtMfK9QKsFi79a4Munc8
+ypUPxE++jhtXcarDy0ta3BXrspIg63a2Ab6C5yv2fC31Ec5v75rUxt9BVxMMQdlX
+h9eMe5FmkgkSBcklOShE2kZvTOPUCSq8DK7JuKsKEH3istPto+PpoyT8RxuZ9K2Q
+rr78CskHtj+TVdFv7TAqn2YDx9ki4HJIxWKVanJ+zOBSrJw=
+-----END CERTIFICATE-----
+\
diff --git a/tests/repository/1/satisfy/t1/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/create-noemail/libfoo-1.0.0.tar.gz
index 32e5a3c..32e5a3c 120000
--- a/tests/repository/1/satisfy/t1/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/auth/create-noemail/libfoo-1.0.0.tar.gz
diff --git a/tests/pkg/1/build2.org/auth/create-noemail/repositories b/tests/pkg/1/build2.org/auth/create-noemail/repositories
new file mode 100644
index 0000000..3645b23
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/create-noemail/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFEzCCAvugAwIBAgIJANxwhIzYRCmBMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoMDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDDA9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNTAxMTYwODQyWhcNMTcwNTAxMTYwODQyWjAzMRcwFQYDVQQKDA5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAwwPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaMqMCgwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMA0GCSqGSIb3DQEBCwUAA4ICAQC4bolUadknEL7ryAGisjFxK0WkH4zNDGrV
+c3c27jHfr7D/GXqMvAkpE9aXwbBDTkgmsvQMkLx4on1WewLFVuDfyvqAS2IGU9i4
+7bbCaACkrhEb1PYvZ8mbVJiXSfnwHSmbEeBbVlej9OqFga2+drv2cI1cqpPZJKhF
+UiUstPYl6vFONE5hLGxKI+6Xg6HYkkc/qawCmY3fuNxC5Hv4n0dwQGdcUqMt5oMK
+1NhfPnunBXY2d/N3SaeBh8PW9Wk5zDq/S2LcO6fgdSho3R3oGbkHj52wPLWK93Vr
+IBU1NhswvR69IOT92UG5KAqHK3VFp8EatuvbODQpvd0uuwIf7BO4Cj7ZPy5R7u8c
+6fwyP1C/jTwSER143NA0LlmjR+qowsicz7zhBI4Fv6dj4rO725lnZ0+Sqok/45SR
+jYdSPZnyWhiWh2ImfMC5INIlWvTtpL6dbfbkR1ZVABVkn3j0Xe4kpsFrmhY693mJ
+LY8PTKJpIZeoj/XrnTDm/NgwcDHpS8j3F++DwZ2UxzJSOgmL9NJlC1lf1ehhxQt8
+33+/zTw6VXwK0PUputdS7xf7z+10Yf4el1kEoAzWZwaz5bwVFOyCQHPCYxuSUKz+
+O0X8FWd2wgPCmU6CB518Cv9cOyUfoZZuV3Va/IyoOUvwNd5Lxen0mo2LPNlXRMVM
+e44+PKmbFA==
+-----END CERTIFICATE-----
+\
diff --git a/tests/repository/1/satisfy/t2/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/expired/libfoo-1.0.0.tar.gz
index 32e5a3c..32e5a3c 120000
--- a/tests/repository/1/satisfy/t2/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/auth/expired/libfoo-1.0.0.tar.gz
diff --git a/tests/pkg/1/build2.org/auth/expired/packages b/tests/pkg/1/build2.org/auth/expired/packages
new file mode 100644
index 0000000..9aaf177
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/expired/packages
@@ -0,0 +1,11 @@
+: 1
+sha256sum: ba75221f5f610f5f9ebee19a7449dfcff5c9fb2c7d87d2482b8324c4ea419950
+:
+name: libfoo
+version: 1.0.0
+summary: libfoo
+license: MIT
+url: http://example.org
+email: pkg@example.org
+location: libfoo-1.0.0.tar.gz
+sha256sum: 642aa60cda80f6579247dfc64192c67ec3f2ed932ae2f56e592000ad9fea435d
diff --git a/tests/pkg/1/build2.org/auth/expired/repositories b/tests/pkg/1/build2.org/auth/expired/repositories
new file mode 100644
index 0000000..8370fa4
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/expired/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAPIoh0763iYdMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoMDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDDA9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNTAxMTA1MDMyWhcNMTYwNTAyMTA1MDMyWjAzMRcwFQYDVQQKDA5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAwwPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAAb+Yxvqi4sOZ+Ti8wcxkxqFrIs32M3XgJIeZYJn9JZbtjuBx0JFZWnL/tz4M
+VTiM2nrUc4qIIpTqEt2If1NcdH0Cjvf6vysLvIOoB4q09B+aW4F+19moixYodoDp
+qq1Ki1MZrBMx+KUICagqRSklPyxaZ2rEFv2TY0pg8/QRFRWGCFgdWJleX6VsqIXC
+KDK6MyHfR1gsDnZZfgr7HJCq1SH3loqlchLBvAF6K/c1LdQyJFYoP4xAt6yaQWzv
+wEqqhhZ7tLpjrjK0K2pqsPoupNcNhE31k+6ZdM5Z5oHT844qKIVNJIdlvFwnQ3ed
+grP6gBCLVY7h7GJ0x2ZG/2hYt4My8RqLxFYHaHAzQsmXFATIzT1Yqz1nw+8V3l5P
+m+rXQNVAw7rgpl3T7klbHVfkg5rPa0kF66PkKq5ax46NS6KVj9sNOqkcXL7huQSG
+BnSLTkaLwwrSZWl+DPXeY8TgUGkkEMY6RvYdqoOGZiZSqtMfK9QKsFi79a4Munc8
+ypUPxE++jhtXcarDy0ta3BXrspIg63a2Ab6C5yv2fC31Ec5v75rUxt9BVxMMQdlX
+h9eMe5FmkgkSBcklOShE2kZvTOPUCSq8DK7JuKsKEH3istPto+PpoyT8RxuZ9K2Q
+rr78CskHtj+TVdFv7TAqn2YDx9ki4HJIxWKVanJ+zOBSrJw=
+-----END CERTIFICATE-----
+\
diff --git a/tests/pkg/1/build2.org/auth/expired/signature b/tests/pkg/1/build2.org/auth/expired/signature
new file mode 100644
index 0000000..dc2aeee
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/expired/signature
@@ -0,0 +1,13 @@
+: 1
+sha256sum: cd05e2ebe9dac484950a54e95a76efba0cac27ec5148dc34fa23a5cd7da72e3d
+signature: \
+geWdw7Gm+Rt+CLDMBby5Y796E8rxwImb0bmcZwGWar9D3vkFm9Kjh00Buuo1PuU7tP1dV6yvRbH8
+NzC0IryEoUJHx9909AJ449ET9Zb+C3ykEeBlKH2wonj7cAVK9ZEDpPEGAtp56XWZQEawl50mwq6t
+XkZAABxtOswXiicdh3HK7kaPHp38/9CBMc0rva6wDnkbTigUYA2ULqLtP5a5mLovVc48zI9A/hmb
+Qx1/Nr7nzTZNDGK7CwTAb1fPam9rZklTfCTPSPUUjvWjM9XdY8cbRE1FrE14TXdyQPxCLzHO2dUO
+YWH5/qMikEoCYhYXQ6KhekoT/MUiVC3PMcYQbYOrOtSxq6RcgnymexBe1XIyld5Rfo1eXu8TK11r
+QPULIqAGy6RwEUhGznuEiGHQwb1UymNyJ/qgr4vBPjJtlvptqG5XNmtiJ22f07nmeVRi2Vg2UyOw
+HoVpy5t/w0tEnUXPA39Vt0v1bUm7Knhc8qL4JFEqK/j/CzEHzEtAjn0aoGuKubCO0WUa+v6ZlkLU
+YrNUIdgT1wgj4yEhLO3g+NsnxFH05D2sfR16rrkI2E6st5crAHR8FOl2FDsWxgKqNbzXZw7rl+Fa
+TobGycX7MDf2mbBmR/KmEkMBJ4mziWLAycSAGyE5VRYDwHPJlQE0143wBzT8eNw4VLm/r+88VYw=
+\
diff --git a/tests/repository/1/depend/stable/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/libfoo-1.0.0.tar.gz
index 28a6a90..28a6a90 100644
--- a/tests/repository/1/depend/stable/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/auth/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/t4c/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/name-mismatch/libfoo-1.0.0.tar.gz
index 32e5a3c..32e5a3c 120000
--- a/tests/repository/1/satisfy/t4c/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/auth/name-mismatch/libfoo-1.0.0.tar.gz
diff --git a/tests/pkg/1/build2.org/auth/name-mismatch/repositories b/tests/pkg/1/build2.org/auth/name-mismatch/repositories
new file mode 100644
index 0000000..15fa092
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/name-mismatch/repositories
@@ -0,0 +1,34 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFXTCCA0WgAwIBAgIJANXKeBuJax30MA0GCSqGSIb3DQEBCwUAMEoxFzAVBgNV
+BAoMDkNvZGUgU3ludGhlc2lzMS8wLQYDVQQDDCZuYW1lOmJ1aWxkMi5vcmcvYXV0
+aC9taXNtYXRjaGVkL25hbWUveDAeFw0xNjA1MDMyMzUyNDJaFw0xNzA1MDMyMzUy
+NDJaMEoxFzAVBgNVBAoMDkNvZGUgU3ludGhlc2lzMS8wLQYDVQQDDCZuYW1lOmJ1
+aWxkMi5vcmcvYXV0aC9taXNtYXRjaGVkL25hbWUveDCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBANq78SXuzFzCMoFU1RnzEeAfzE0UUYGynS3F2lG7viH3
+coxjLt+BrFBudVs4XDTpjXS19hRxIohEgD71W1jhDvmUC9yCMW13PCIIjRKTTz0e
+fEhTcMHdhOgvKZsje0IV7svoKVXcG7DfUVl51wWPQPSbUrfsQbsXg7Pz5HaDx+Dt
+2i9hwdE1M0z4R2dtwQkszFyCKiX8RF9oPXirTz5ETLC3f19JUapLrY5l5ZylzQif
+LhPMlHLlrT0n7KkohH7waX3KyeLa0M2IIl3zaeAsuN+ErFVecAdlJIvX00cth2OO
+/Gxy09sIKlagi2q7ZDik2sMvG8dAv7gNZsXp+FOj/XXCiOI9f6D5ospJdK9B5UCA
+BjmGc8W5Odv6ZLey5Ui76luI7ciITOKfAoEkbyMiNHiRxLdM7aAeizdcwHU4bm6J
+lmiJk8UyyV85f33mvCSfuo7D+DQYiK650/xwRdTFBIqi38IwME62gT7ah/AOmiPs
+hj7FjwIU7ZWHskyr9qpExQOEKJXoLZJo1rf6MRc8AsJyz6zdfQhT1BTzhogNfru4
+xjVM6fSrjRUF34msuWcz/HKo9W350Aw2y5F59kziP+m7G6uBYrqmElv/13Vamg2Z
+Z1b38KMz5Ss3SkfcDErOzz/D+0hRlOaCIeWts1G2zWcQvBnn+zGA+sTIu0xAFOCR
+AgMBAAGjRjBEMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
+AzAaBgNVHREEEzARgQ9pbmZvQGJ1aWxkMi5vcmcwDQYJKoZIhvcNAQELBQADggIB
+AKvUnaZP20bf4ahpa50QUvRb4Kel7yhvvmae7/aIBk5ANxnJC3yuOpmtGve5+lLE
++6w/XwFASz/7PAdUlEp+LBYknGnx5StoqBzP7nwapm+tBcrZ3Q5MxareXXOSx1Ac
+4Dh9vDMRevDHZ5LQATHG+Q7zaKmBb0ZeSjJlP4+0680AKN4YDmY1a81uoa1jRXbV
+c4iutSoBaVNdWB5+GGelEjvhzN0gZ22IpS3mjbyCbJNywHQMHBLwtxuFgkdOfPkC
+MGBbvG5TD7SKgQNlfsoQu3UJpokzz7hP1TmjCpSDjGSqJlYvovrHCFQGWjtk0UVQ
+tXAQiVPlMsj2kbxJf2ICYYZuvQbmhvNF049ldrpk26jlDzSZ+5t2jueC+q8knTsn
+p3LPx7sqGfkIq41pxHAQseBSYUEo6r6LwfDQPmwb7fOUpLJm6GdsEkqqUhmB3W6Y
+H4t6PKCjFI91JqTka6oF3Lblf3QLXlMpevBwvgaZAC1d4eiNPq+mSAL6T2Cb8Hvq
+Imzyh6EcMbWD9RSnbDCujBjnj0WXO7VjUqShZ60D78PCdXbaK7RAOX9cLUYO/OHd
+z5vUbBrPvDImu5gJFRLOGKKw/7Yn9um/pyuSHuuucBkjBJ4J/tPi/MBZiWM7g2qc
+obzYoLev74dMkZMZDsGDAcuYX40ZVkF7ATqZC405/fSH
+-----END CERTIFICATE-----
+\
diff --git a/tests/pkg/1/build2.org/auth/sha256sum-mismatch/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/sha256sum-mismatch/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/sha256sum-mismatch/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/pkg/1/build2.org/auth/sha256sum-mismatch/repositories b/tests/pkg/1/build2.org/auth/sha256sum-mismatch/repositories
new file mode 100644
index 0000000..9bcb20a
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/sha256sum-mismatch/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAK41T2Si9cMEMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoTDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDEw9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNDMwMTQyNDQ3WhcNMTcwNDMwMTQyNDQ3WjAzMRcwFQYDVQQKEw5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAxMPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAeBHkz12OPjjFPftCZ8u/9Kq+W0ZnqQqXmSLj5SQFiG5PEUSV19XFcGawwjbU
+6guceUSHhHQr/KhpV02pyoPPtskdognHQCPr6HCR6eeEtfBUSum5dKpJAWqGhszg
+bPCb11/ksBBzpBbc/C+gnU8ZvsglJ/wxrizZ0AYeSdvyWq8pgoh2vGpgdhK5MWhd
+rVGQm+TUVmXyQwNrYDjqhoGZF02UilIekedegWUEvkXnk5rzyzTaX+GwlnsplVY2
+Xijn8cWkO4cCDkthyjfNPgUm8ZgKuBgs+OYRbW4XtuXgeBucBVCryrM8+3OSmzbs
+IBQWL0EDT7bnt2ukE+cUx8Foc/+180KZAdDbUY9MOsYCac3bXnIJd/q1pzkr+OvL
+EXnuIGuzUqVkcD4+ABaWefNUm3KeZwBAupYc9eo2/YzpCUsgAe0PAdgJhcg9D4kX
+2RwwjVpOPWUxevrPq32Lo3R8obfl2aCLzcuiuheb1EcJLlAH67KqdXLDpvZqJ9qi
+iaBCixRl2t3our5VSBWQQefGnH6DWn1JM34zyBbeZdgjyqebSN42p+hADYe2qzdf
+AviHJsBwvt3HYpr3bfU6EDx1cU5D6gNAxzZMkZhnEQ7u6kXDcLyWJhgXcZhGT0xv
+D/x+fIGI1t2XkoOmvuno1MqoDQX3sCYd0bfB/9wwzcNexic=
+-----END CERTIFICATE-----
+\
diff --git a/tests/pkg/1/build2.org/auth/signature-mismatch/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/signature-mismatch/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/signature-mismatch/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/pkg/1/build2.org/auth/signature-mismatch/repositories b/tests/pkg/1/build2.org/auth/signature-mismatch/repositories
new file mode 100644
index 0000000..9bcb20a
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/signature-mismatch/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAK41T2Si9cMEMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoTDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDEw9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNDMwMTQyNDQ3WhcNMTcwNDMwMTQyNDQ3WjAzMRcwFQYDVQQKEw5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAxMPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAeBHkz12OPjjFPftCZ8u/9Kq+W0ZnqQqXmSLj5SQFiG5PEUSV19XFcGawwjbU
+6guceUSHhHQr/KhpV02pyoPPtskdognHQCPr6HCR6eeEtfBUSum5dKpJAWqGhszg
+bPCb11/ksBBzpBbc/C+gnU8ZvsglJ/wxrizZ0AYeSdvyWq8pgoh2vGpgdhK5MWhd
+rVGQm+TUVmXyQwNrYDjqhoGZF02UilIekedegWUEvkXnk5rzyzTaX+GwlnsplVY2
+Xijn8cWkO4cCDkthyjfNPgUm8ZgKuBgs+OYRbW4XtuXgeBucBVCryrM8+3OSmzbs
+IBQWL0EDT7bnt2ukE+cUx8Foc/+180KZAdDbUY9MOsYCac3bXnIJd/q1pzkr+OvL
+EXnuIGuzUqVkcD4+ABaWefNUm3KeZwBAupYc9eo2/YzpCUsgAe0PAdgJhcg9D4kX
+2RwwjVpOPWUxevrPq32Lo3R8obfl2aCLzcuiuheb1EcJLlAH67KqdXLDpvZqJ9qi
+iaBCixRl2t3our5VSBWQQefGnH6DWn1JM34zyBbeZdgjyqebSN42p+hADYe2qzdf
+AviHJsBwvt3HYpr3bfU6EDx1cU5D6gNAxzZMkZhnEQ7u6kXDcLyWJhgXcZhGT0xv
+D/x+fIGI1t2XkoOmvuno1MqoDQX3sCYd0bfB/9wwzcNexic=
+-----END CERTIFICATE-----
+\
diff --git a/tests/pkg/1/build2.org/auth/signed/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/signed/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/signed/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/pkg/1/build2.org/auth/signed/repositories b/tests/pkg/1/build2.org/auth/signed/repositories
new file mode 100644
index 0000000..9bcb20a
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/signed/repositories
@@ -0,0 +1,33 @@
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAK41T2Si9cMEMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoTDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDEw9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNDMwMTQyNDQ3WhcNMTcwNDMwMTQyNDQ3WjAzMRcwFQYDVQQKEw5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAxMPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAeBHkz12OPjjFPftCZ8u/9Kq+W0ZnqQqXmSLj5SQFiG5PEUSV19XFcGawwjbU
+6guceUSHhHQr/KhpV02pyoPPtskdognHQCPr6HCR6eeEtfBUSum5dKpJAWqGhszg
+bPCb11/ksBBzpBbc/C+gnU8ZvsglJ/wxrizZ0AYeSdvyWq8pgoh2vGpgdhK5MWhd
+rVGQm+TUVmXyQwNrYDjqhoGZF02UilIekedegWUEvkXnk5rzyzTaX+GwlnsplVY2
+Xijn8cWkO4cCDkthyjfNPgUm8ZgKuBgs+OYRbW4XtuXgeBucBVCryrM8+3OSmzbs
+IBQWL0EDT7bnt2ukE+cUx8Foc/+180KZAdDbUY9MOsYCac3bXnIJd/q1pzkr+OvL
+EXnuIGuzUqVkcD4+ABaWefNUm3KeZwBAupYc9eo2/YzpCUsgAe0PAdgJhcg9D4kX
+2RwwjVpOPWUxevrPq32Lo3R8obfl2aCLzcuiuheb1EcJLlAH67KqdXLDpvZqJ9qi
+iaBCixRl2t3our5VSBWQQefGnH6DWn1JM34zyBbeZdgjyqebSN42p+hADYe2qzdf
+AviHJsBwvt3HYpr3bfU6EDx1cU5D6gNAxzZMkZhnEQ7u6kXDcLyWJhgXcZhGT0xv
+D/x+fIGI1t2XkoOmvuno1MqoDQX3sCYd0bfB/9wwzcNexic=
+-----END CERTIFICATE-----
+\
diff --git a/tests/pkg/1/build2.org/auth/unsigned1/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/unsigned1/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/unsigned1/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/depend/stable/repositories b/tests/pkg/1/build2.org/auth/unsigned1/repositories
index 5b70556..5b70556 100644
--- a/tests/repository/1/depend/stable/repositories
+++ b/tests/pkg/1/build2.org/auth/unsigned1/repositories
diff --git a/tests/pkg/1/build2.org/auth/unsigned2/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/auth/unsigned2/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/auth/unsigned2/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/fetch/repositories b/tests/pkg/1/build2.org/auth/unsigned2/repositories
index 5b70556..5b70556 100644
--- a/tests/repository/1/fetch/repositories
+++ b/tests/pkg/1/build2.org/auth/unsigned2/repositories
diff --git a/tests/repository/1/common/bar/stable/libbar-1.0.0.tar.gz b/tests/pkg/1/build2.org/common/bar/stable/libbar-1.0.0.tar.gz
index d77c1ba..d77c1ba 100644
--- a/tests/repository/1/common/bar/stable/libbar-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/common/bar/stable/libbar-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/common/bar/stable/repositories b/tests/pkg/1/build2.org/common/bar/stable/repositories
index b8ecc1f..b8ecc1f 100644
--- a/tests/repository/1/common/bar/stable/repositories
+++ b/tests/pkg/1/build2.org/common/bar/stable/repositories
diff --git a/tests/repository/1/common/bar/testing/libbar-1.1.0.tar.gz b/tests/pkg/1/build2.org/common/bar/testing/libbar-1.1.0.tar.gz
index f081804..f081804 100644
--- a/tests/repository/1/common/bar/testing/libbar-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/common/bar/testing/libbar-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/common/bar/testing/repositories b/tests/pkg/1/build2.org/common/bar/testing/repositories
index 5ab8cba..5ab8cba 100644
--- a/tests/repository/1/common/bar/testing/repositories
+++ b/tests/pkg/1/build2.org/common/bar/testing/repositories
diff --git a/tests/repository/1/common/bar/unstable/libbar-1.1.1.tar.gz b/tests/pkg/1/build2.org/common/bar/unstable/libbar-1.1.1.tar.gz
index a0fbdc3..a0fbdc3 100644
--- a/tests/repository/1/common/bar/unstable/libbar-1.1.1.tar.gz
+++ b/tests/pkg/1/build2.org/common/bar/unstable/libbar-1.1.1.tar.gz
Binary files differ
diff --git a/tests/repository/1/common/bar/unstable/repositories b/tests/pkg/1/build2.org/common/bar/unstable/repositories
index d4ddcf5..d4ddcf5 100644
--- a/tests/repository/1/common/bar/unstable/repositories
+++ b/tests/pkg/1/build2.org/common/bar/unstable/repositories
diff --git a/tests/repository/1/common/foo/stable/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/common/foo/stable/libfoo-1.0.0.tar.gz
index c29dd9d..c29dd9d 100644
--- a/tests/repository/1/common/foo/stable/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/common/foo/stable/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/common/foo/stable/repositories b/tests/pkg/1/build2.org/common/foo/stable/repositories
index 13673fb..13673fb 100644
--- a/tests/repository/1/common/foo/stable/repositories
+++ b/tests/pkg/1/build2.org/common/foo/stable/repositories
diff --git a/tests/repository/1/common/foo/testing/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/common/foo/testing/libfoo-1.1.0.tar.gz
index a6c0202..a6c0202 100644
--- a/tests/repository/1/common/foo/testing/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/common/foo/testing/libfoo-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/common/foo/testing/repositories b/tests/pkg/1/build2.org/common/foo/testing/repositories
index cd33f88..cd33f88 100644
--- a/tests/repository/1/common/foo/testing/repositories
+++ b/tests/pkg/1/build2.org/common/foo/testing/repositories
diff --git a/tests/repository/1/common/hello/libhello-1.0.0+1.tar.gz b/tests/pkg/1/build2.org/common/hello/libhello-1.0.0+1.tar.gz
index eeff2a0..eeff2a0 100644
--- a/tests/repository/1/common/hello/libhello-1.0.0+1.tar.gz
+++ b/tests/pkg/1/build2.org/common/hello/libhello-1.0.0+1.tar.gz
Binary files differ
diff --git a/tests/pkg/1/build2.org/common/hello/repositories b/tests/pkg/1/build2.org/common/hello/repositories
new file mode 100644
index 0000000..bbe8a66
--- /dev/null
+++ b/tests/pkg/1/build2.org/common/hello/repositories
@@ -0,0 +1,36 @@
+# Hello World repository. Currently does not have any prerequisite
+# repositories.
+#
+: 1
+certificate: \
+-----BEGIN CERTIFICATE-----
+MIIFLzCCAxegAwIBAgIJAK41T2Si9cMEMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+BAoTDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDEw9uYW1lOmJ1aWxkMi5vcmcwHhcN
+MTYwNDMwMTQyNDQ3WhcNMTcwNDMwMTQyNDQ3WjAzMRcwFQYDVQQKEw5Db2RlIFN5
+bnRoZXNpczEYMBYGA1UEAxMPbmFtZTpidWlsZDIub3JnMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2rvxJe7MXMIygVTVGfMR4B/MTRRRgbKdLcXaUbu+
+IfdyjGMu34GsUG51WzhcNOmNdLX2FHEiiESAPvVbWOEO+ZQL3IIxbXc8IgiNEpNP
+PR58SFNwwd2E6C8pmyN7QhXuy+gpVdwbsN9RWXnXBY9A9JtSt+xBuxeDs/PkdoPH
+4O3aL2HB0TUzTPhHZ23BCSzMXIIqJfxEX2g9eKtPPkRMsLd/X0lRqkutjmXlnKXN
+CJ8uE8yUcuWtPSfsqSiEfvBpfcrJ4trQzYgiXfNp4Cy434SsVV5wB2Uki9fTRy2H
+Y478bHLT2wgqVqCLartkOKTawy8bx0C/uA1mxen4U6P9dcKI4j1/oPmiykl0r0Hl
+QIAGOYZzxbk52/pkt7LlSLvqW4jtyIhM4p8CgSRvIyI0eJHEt0ztoB6LN1zAdThu
+bomWaImTxTLJXzl/fea8JJ+6jsP4NBiIrrnT/HBF1MUEiqLfwjAwTraBPtqH8A6a
+I+yGPsWPAhTtlYeyTKv2qkTFA4QolegtkmjWt/oxFzwCwnLPrN19CFPUFPOGiA1+
+u7jGNUzp9KuNFQXfiay5ZzP8cqj1bfnQDDbLkXn2TOI/6bsbq4FiuqYSW//XdVqa
+DZlnVvfwozPlKzdKR9wMSs7PP8P7SFGU5oIh5a2zUbbNZxC8Gef7MYD6xMi7TEAU
+4JECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+BwMDMBoGA1UdEQQTMBGBD2luZm9AYnVpbGQyLm9yZzANBgkqhkiG9w0BAQsFAAOC
+AgEAeBHkz12OPjjFPftCZ8u/9Kq+W0ZnqQqXmSLj5SQFiG5PEUSV19XFcGawwjbU
+6guceUSHhHQr/KhpV02pyoPPtskdognHQCPr6HCR6eeEtfBUSum5dKpJAWqGhszg
+bPCb11/ksBBzpBbc/C+gnU8ZvsglJ/wxrizZ0AYeSdvyWq8pgoh2vGpgdhK5MWhd
+rVGQm+TUVmXyQwNrYDjqhoGZF02UilIekedegWUEvkXnk5rzyzTaX+GwlnsplVY2
+Xijn8cWkO4cCDkthyjfNPgUm8ZgKuBgs+OYRbW4XtuXgeBucBVCryrM8+3OSmzbs
+IBQWL0EDT7bnt2ukE+cUx8Foc/+180KZAdDbUY9MOsYCac3bXnIJd/q1pzkr+OvL
+EXnuIGuzUqVkcD4+ABaWefNUm3KeZwBAupYc9eo2/YzpCUsgAe0PAdgJhcg9D4kX
+2RwwjVpOPWUxevrPq32Lo3R8obfl2aCLzcuiuheb1EcJLlAH67KqdXLDpvZqJ9qi
+iaBCixRl2t3our5VSBWQQefGnH6DWn1JM34zyBbeZdgjyqebSN42p+hADYe2qzdf
+AviHJsBwvt3HYpr3bfU6EDx1cU5D6gNAxzZMkZhnEQ7u6kXDcLyWJhgXcZhGT0xv
+D/x+fIGI1t2XkoOmvuno1MqoDQX3sCYd0bfB/9wwzcNexic=
+-----END CERTIFICATE-----
+\
diff --git a/tests/repository/1/common/libhello-1.0.0+1/build/bootstrap.build b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/bootstrap.build
index c9bf24d..c9bf24d 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/build/bootstrap.build
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/bootstrap.build
diff --git a/tests/repository/1/common/libhello-1.0.0+1/build/export.build b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/export.build
index 1eb73b7..1eb73b7 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/build/export.build
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/export.build
diff --git a/tests/repository/1/common/libhello-1.0.0+1/build/root.build b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/root.build
index ebe16d5..ebe16d5 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/build/root.build
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/build/root.build
diff --git a/tests/repository/1/common/libhello-1.0.0+1/buildfile b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/buildfile
index f846f89..f846f89 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/buildfile
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/buildfile
diff --git a/tests/repository/1/common/libhello-1.0.0+1/hello/buildfile b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/buildfile
index 4903a33..4903a33 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/hello/buildfile
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/buildfile
diff --git a/tests/repository/1/common/libhello-1.0.0+1/hello/hello b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello
index 4f85114..4f85114 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/hello/hello
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello
diff --git a/tests/repository/1/common/libhello-1.0.0+1/hello/hello.cxx b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello.cxx
index 65d0aa7..65d0aa7 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/hello/hello.cxx
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/hello/hello.cxx
diff --git a/tests/repository/1/common/libhello-1.0.0+1/manifest b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/manifest
index 82d79c8..82d79c8 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/manifest
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/manifest
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/build/bootstrap.build b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/bootstrap.build
index 2c2de24..2c2de24 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/build/bootstrap.build
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/bootstrap.build
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/build/root.build b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/root.build
index 672eda8..672eda8 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/build/root.build
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/build/root.build
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/buildfile b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/buildfile
index 4dcbe2a..4dcbe2a 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/buildfile
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/buildfile
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/test/buildfile b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/buildfile
index a46969d..a46969d 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/test/buildfile
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/buildfile
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/test/driver.cxx b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/driver.cxx
index 59bfb2d..59bfb2d 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/test/driver.cxx
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/driver.cxx
diff --git a/tests/repository/1/common/libhello-1.0.0+1/tests/test/test.out b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/test.out
index 8ab686e..8ab686e 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/tests/test/test.out
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/tests/test/test.out
diff --git a/tests/repository/1/common/libhello-1.0.0+1/version b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/version
index 3eefcb9..3eefcb9 100644
--- a/tests/repository/1/common/libhello-1.0.0+1/version
+++ b/tests/pkg/1/build2.org/common/libhello-1.0.0+1/version
diff --git a/tests/repository/1/common/not-a-package.tar.gz b/tests/pkg/1/build2.org/common/not-a-package.tar.gz
index aa10905..aa10905 100644
--- a/tests/repository/1/common/not-a-package.tar.gz
+++ b/tests/pkg/1/build2.org/common/not-a-package.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libbar-1.0.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libbar-1.0.0.tar.gz
index 0ae6e26..0ae6e26 100644
--- a/tests/repository/1/depend/stable/libbar-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libbar-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libbar-1.1.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libbar-1.1.0.tar.gz
index 39dbdf4..39dbdf4 100644
--- a/tests/repository/1/depend/stable/libbar-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libbar-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libbar-1.2.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libbar-1.2.0.tar.gz
index 3a034ff..3a034ff 100644
--- a/tests/repository/1/depend/stable/libbar-1.2.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libbar-1.2.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libbar-1.3.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libbar-1.3.0.tar.gz
index 19d5a6f..19d5a6f 100644
--- a/tests/repository/1/depend/stable/libbar-1.3.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libbar-1.3.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/fetch/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libfoo-1.0.0.tar.gz
index 28a6a90..28a6a90 100644
--- a/tests/repository/1/fetch/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libfoo-1.1.0.tar.gz
index e03481f..e03481f 100644
--- a/tests/repository/1/depend/stable/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libfoo-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/depend/stable/libfoo-1.2.0.tar.gz b/tests/pkg/1/build2.org/depend/stable/libfoo-1.2.0.tar.gz
index aad6906..aad6906 100644
--- a/tests/repository/1/depend/stable/libfoo-1.2.0.tar.gz
+++ b/tests/pkg/1/build2.org/depend/stable/libfoo-1.2.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/repositories b/tests/pkg/1/build2.org/depend/stable/repositories
index 5b70556..5b70556 100644
--- a/tests/repository/1/satisfy/repositories
+++ b/tests/pkg/1/build2.org/depend/stable/repositories
diff --git a/tests/repository/1/satisfy/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
index 28a6a90..28a6a90 100644
--- a/tests/repository/1/satisfy/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/fetch/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0.tar.gz
index e03481f..e03481f 100644
--- a/tests/repository/1/fetch/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/fetch/libfoo-1.1.0/build/bootstrap.build b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/build/bootstrap.build
index 54f267e..54f267e 100644
--- a/tests/repository/1/fetch/libfoo-1.1.0/build/bootstrap.build
+++ b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/build/bootstrap.build
diff --git a/tests/repository/1/fetch/libfoo-1.1.0/buildfile b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/buildfile
index b3ec74f..b3ec74f 100644
--- a/tests/repository/1/fetch/libfoo-1.1.0/buildfile
+++ b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/buildfile
diff --git a/tests/repository/1/fetch/libfoo-1.1.0/manifest b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/manifest
index 3453757..3453757 100644
--- a/tests/repository/1/fetch/libfoo-1.1.0/manifest
+++ b/tests/pkg/1/build2.org/fetch/libfoo-1.1.0/manifest
diff --git a/tests/repository/1/status/stable/repositories b/tests/pkg/1/build2.org/fetch/repositories
index 5b70556..5b70556 100644
--- a/tests/repository/1/status/stable/repositories
+++ b/tests/pkg/1/build2.org/fetch/repositories
diff --git a/tests/pkg/1/build2.org/fetch/t1/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/fetch/t1/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/fetch/t1/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/fetch/t1/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/fetch/t1/libfoo-1.1.0.tar.gz
index c004b2a..c004b2a 120000
--- a/tests/repository/1/fetch/t1/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/fetch/t1/libfoo-1.1.0.tar.gz
diff --git a/tests/repository/1/fetch/t1/repositories b/tests/pkg/1/build2.org/fetch/t1/repositories
index d965b15..d965b15 120000
--- a/tests/repository/1/fetch/t1/repositories
+++ b/tests/pkg/1/build2.org/fetch/t1/repositories
diff --git a/tests/repository/1/satisfy/libbar-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbar-1.0.0.tar.gz
index 5dc3a9b..5dc3a9b 100644
--- a/tests/repository/1/satisfy/libbar-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbar-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libbar-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbar-1.1.0.tar.gz
index 881292e..881292e 100644
--- a/tests/repository/1/satisfy/libbar-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbar-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libbar-1.2.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbar-1.2.0.tar.gz
index 4572395..4572395 100644
--- a/tests/repository/1/satisfy/libbar-1.2.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbar-1.2.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libbaz-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbaz-1.0.0.tar.gz
index 1de32b2..1de32b2 100644
--- a/tests/repository/1/satisfy/libbaz-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbaz-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libbaz-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbaz-1.1.0.tar.gz
index 1aa72a8..1aa72a8 100644
--- a/tests/repository/1/satisfy/libbaz-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbaz-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libbiz-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libbiz-1.0.0.tar.gz
index 42e3db4..42e3db4 100644
--- a/tests/repository/1/satisfy/libbiz-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libbiz-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libfoo-0.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz
index befd2a4..befd2a4 100644
--- a/tests/repository/1/satisfy/libfoo-0.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz
Binary files differ
diff --git a/tests/pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz
new file mode 100644
index 0000000..28a6a90
--- /dev/null
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0.tar.gz
index e03481f..e03481f 100644
--- a/tests/repository/1/satisfy/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libfoo-1.1.0/build/bootstrap.build b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/build/bootstrap.build
index b24ee6a..b24ee6a 100644
--- a/tests/repository/1/satisfy/libfoo-1.1.0/build/bootstrap.build
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/build/bootstrap.build
diff --git a/tests/repository/1/satisfy/libfoo-1.1.0/buildfile b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/buildfile
index b3ec74f..b3ec74f 100644
--- a/tests/repository/1/satisfy/libfoo-1.1.0/buildfile
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/buildfile
diff --git a/tests/repository/1/satisfy/libfoo-1.1.0/manifest b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/manifest
index 3453757..3453757 100644
--- a/tests/repository/1/satisfy/libfoo-1.1.0/manifest
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.1.0/manifest
diff --git a/tests/repository/1/satisfy/libfoo-1.2.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libfoo-1.2.0.tar.gz
index 80ce03b..80ce03b 100644
--- a/tests/repository/1/satisfy/libfoo-1.2.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libfoo-1.2.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/satisfy/libfox-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/libfox-1.0.0.tar.gz
index 9ba7a31..9ba7a31 100644
--- a/tests/repository/1/satisfy/libfox-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/libfox-1.0.0.tar.gz
Binary files differ
diff --git a/tests/pkg/1/build2.org/satisfy/repositories b/tests/pkg/1/build2.org/satisfy/repositories
new file mode 100644
index 0000000..5b70556
--- /dev/null
+++ b/tests/pkg/1/build2.org/satisfy/repositories
@@ -0,0 +1 @@
+: 1
diff --git a/tests/pkg/1/build2.org/satisfy/t1/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t1/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/satisfy/t1/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/satisfy/t1/repositories b/tests/pkg/1/build2.org/satisfy/t1/repositories
index d965b15..d965b15 120000
--- a/tests/repository/1/satisfy/t1/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t1/repositories
diff --git a/tests/repository/1/satisfy/t2/libbar-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t2/libbar-1.0.0.tar.gz
index 93e8c71..93e8c71 120000
--- a/tests/repository/1/satisfy/t2/libbar-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t2/libbar-1.0.0.tar.gz
diff --git a/tests/pkg/1/build2.org/satisfy/t2/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t2/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/satisfy/t2/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/satisfy/t2/repositories b/tests/pkg/1/build2.org/satisfy/t2/repositories
index d965b15..d965b15 120000
--- a/tests/repository/1/satisfy/t2/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t2/repositories
diff --git a/tests/repository/1/satisfy/t3/libbaz-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t3/libbaz-1.0.0.tar.gz
index 189242a..189242a 120000
--- a/tests/repository/1/satisfy/t3/libbaz-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t3/libbaz-1.0.0.tar.gz
diff --git a/tests/repository/1/satisfy/t3/libfox-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t3/libfox-1.0.0.tar.gz
index dcfd7aa..dcfd7aa 120000
--- a/tests/repository/1/satisfy/t3/libfox-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t3/libfox-1.0.0.tar.gz
diff --git a/tests/repository/1/satisfy/t3/repositories b/tests/pkg/1/build2.org/satisfy/t3/repositories
index d65b272..d65b272 100644
--- a/tests/repository/1/satisfy/t3/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t3/repositories
diff --git a/tests/repository/1/satisfy/t4a/libfoo-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4a/libfoo-1.1.0.tar.gz
index c004b2a..c004b2a 120000
--- a/tests/repository/1/satisfy/t4a/libfoo-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t4a/libfoo-1.1.0.tar.gz
diff --git a/tests/repository/1/satisfy/t4a/repositories b/tests/pkg/1/build2.org/satisfy/t4a/repositories
index d965b15..d965b15 120000
--- a/tests/repository/1/satisfy/t4a/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t4a/repositories
diff --git a/tests/repository/1/satisfy/t4b/libbar-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4b/libbar-1.1.0.tar.gz
index b9a2de5..b9a2de5 120000
--- a/tests/repository/1/satisfy/t4b/libbar-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t4b/libbar-1.1.0.tar.gz
diff --git a/tests/repository/1/satisfy/t4b/repositories b/tests/pkg/1/build2.org/satisfy/t4b/repositories
index 7b85e71..7b85e71 100644
--- a/tests/repository/1/satisfy/t4b/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t4b/repositories
diff --git a/tests/repository/1/satisfy/t4c/libbaz-1.1.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4c/libbaz-1.1.0.tar.gz
index 0edbce7..0edbce7 120000
--- a/tests/repository/1/satisfy/t4c/libbaz-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t4c/libbaz-1.1.0.tar.gz
diff --git a/tests/pkg/1/build2.org/satisfy/t4c/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4c/libfoo-1.0.0.tar.gz
new file mode 120000
index 0000000..32e5a3c
--- /dev/null
+++ b/tests/pkg/1/build2.org/satisfy/t4c/libfoo-1.0.0.tar.gz
@@ -0,0 +1 @@
+../libfoo-1.0.0.tar.gz \ No newline at end of file
diff --git a/tests/repository/1/satisfy/t4c/repositories b/tests/pkg/1/build2.org/satisfy/t4c/repositories
index 26c0e93..26c0e93 100644
--- a/tests/repository/1/satisfy/t4c/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t4c/repositories
diff --git a/tests/repository/1/satisfy/t4d/libbiz-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4d/libbiz-1.0.0.tar.gz
index 70c2fda..70c2fda 120000
--- a/tests/repository/1/satisfy/t4d/libbiz-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t4d/libbiz-1.0.0.tar.gz
diff --git a/tests/repository/1/satisfy/t4d/libfox-1.0.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t4d/libfox-1.0.0.tar.gz
index dcfd7aa..dcfd7aa 120000
--- a/tests/repository/1/satisfy/t4d/libfox-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t4d/libfox-1.0.0.tar.gz
diff --git a/tests/repository/1/satisfy/t4d/repositories b/tests/pkg/1/build2.org/satisfy/t4d/repositories
index f0e1983..f0e1983 100644
--- a/tests/repository/1/satisfy/t4d/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t4d/repositories
diff --git a/tests/repository/1/satisfy/t5/libbar-1.2.0.tar.gz b/tests/pkg/1/build2.org/satisfy/t5/libbar-1.2.0.tar.gz
index b4a7773..b4a7773 120000
--- a/tests/repository/1/satisfy/t5/libbar-1.2.0.tar.gz
+++ b/tests/pkg/1/build2.org/satisfy/t5/libbar-1.2.0.tar.gz
diff --git a/tests/repository/1/satisfy/t5/repositories b/tests/pkg/1/build2.org/satisfy/t5/repositories
index d965b15..d965b15 120000
--- a/tests/repository/1/satisfy/t5/repositories
+++ b/tests/pkg/1/build2.org/satisfy/t5/repositories
diff --git a/tests/repository/1/status/extra/libbar-1.1.0+1.tar.gz b/tests/pkg/1/build2.org/status/extra/libbar-1.1.0+1.tar.gz
index 890e9e2..890e9e2 100644
--- a/tests/repository/1/status/extra/libbar-1.1.0+1.tar.gz
+++ b/tests/pkg/1/build2.org/status/extra/libbar-1.1.0+1.tar.gz
Binary files differ
diff --git a/tests/repository/1/status/extra/repositories b/tests/pkg/1/build2.org/status/extra/repositories
index ecaa454..ecaa454 100644
--- a/tests/repository/1/status/extra/repositories
+++ b/tests/pkg/1/build2.org/status/extra/repositories
diff --git a/tests/repository/1/status/stable/libbar-1.0.0.tar.gz b/tests/pkg/1/build2.org/status/stable/libbar-1.0.0.tar.gz
index 97e6e32..97e6e32 100644
--- a/tests/repository/1/status/stable/libbar-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/status/stable/libbar-1.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/status/stable/libfoo-1.0.0.tar.gz b/tests/pkg/1/build2.org/status/stable/libfoo-1.0.0.tar.gz
index 5e7fa17..5e7fa17 100644
--- a/tests/repository/1/status/stable/libfoo-1.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/status/stable/libfoo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/pkg/1/build2.org/status/stable/repositories b/tests/pkg/1/build2.org/status/stable/repositories
new file mode 100644
index 0000000..5b70556
--- /dev/null
+++ b/tests/pkg/1/build2.org/status/stable/repositories
@@ -0,0 +1 @@
+: 1
diff --git a/tests/repository/1/status/testing/libbar-1.0.0+1.tar.gz b/tests/pkg/1/build2.org/status/testing/libbar-1.0.0+1.tar.gz
index 5794085..5794085 100644
--- a/tests/repository/1/status/testing/libbar-1.0.0+1.tar.gz
+++ b/tests/pkg/1/build2.org/status/testing/libbar-1.0.0+1.tar.gz
Binary files differ
diff --git a/tests/repository/1/status/testing/libbar-1.1.0.tar.gz b/tests/pkg/1/build2.org/status/testing/libbar-1.1.0.tar.gz
index b01ac44..b01ac44 100644
--- a/tests/repository/1/status/testing/libbar-1.1.0.tar.gz
+++ b/tests/pkg/1/build2.org/status/testing/libbar-1.1.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/status/testing/repositories b/tests/pkg/1/build2.org/status/testing/repositories
index 7bd7269..7bd7269 100644
--- a/tests/repository/1/status/testing/repositories
+++ b/tests/pkg/1/build2.org/status/testing/repositories
diff --git a/tests/repository/1/status/unstable/libbar-2.0.0.tar.gz b/tests/pkg/1/build2.org/status/unstable/libbar-2.0.0.tar.gz
index 6cc5890..6cc5890 100644
--- a/tests/repository/1/status/unstable/libbar-2.0.0.tar.gz
+++ b/tests/pkg/1/build2.org/status/unstable/libbar-2.0.0.tar.gz
Binary files differ
diff --git a/tests/repository/1/status/unstable/repositories b/tests/pkg/1/build2.org/status/unstable/repositories
index 8f4a5f6..8f4a5f6 100644
--- a/tests/repository/1/status/unstable/repositories
+++ b/tests/pkg/1/build2.org/status/unstable/repositories
diff --git a/tests/repository/publish b/tests/pkg/publish
index 5d3d0f1..00453cc 100755
--- a/tests/repository/publish
+++ b/tests/pkg/publish
@@ -12,5 +12,6 @@ rsync -v -rlpt --copy-unsafe-links \
--include '*.tar.gz' \
--include 'packages' \
--include 'repositories' \
+--include 'signature' \
--exclude '*' \
-1/ build2.org:/var/bpkg/1/tests/
+1/build2.org/ build2.org:/var/pkg/1/
diff --git a/tests/repository/.gitignore b/tests/repository/.gitignore
deleted file mode 100644
index f9ced93..0000000
--- a/tests/repository/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-packages
diff --git a/tests/repository/1/common/hello/repositories b/tests/repository/1/common/hello/repositories
deleted file mode 100644
index eb30fe1..0000000
--- a/tests/repository/1/common/hello/repositories
+++ /dev/null
@@ -1,4 +0,0 @@
-# Hello World repository. Currently does not have any prerequisite
-# repositories.
-#
-: 1
diff --git a/tests/test.sh b/tests/test.sh
index 0cdcd4f..84ad8be 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -13,7 +13,7 @@
# --remote
# Test using the remote repositories. Normally, you would first run the
# local test in order to create the repositories, then publish them (see
-# repository/publish.sh), and finally run the remote test.
+# pkg/publish.sh), and finally run the remote test.
#
# --valgrind
# Run under valgrind (takes forever).
@@ -79,15 +79,18 @@ fi
bpkg="$bpkg $options"
-# Repository location, name, and absolute location prefixes.
+# Repository location, name, and absolute location prefixes. Note that the
+# local path is carefully crafted so that we end up with the same repository
+# names in both cases. This is necessary for the authentication tests to work
+# in both cases.
#
if [ "$remote" = "y" ]; then
- rep=https://build2.org/pkg/1/tests
- repn=build2.org/tests/
+ rep=https://build2.org/bpkg/1
+ repn=build2.org/
repa=$rep
else
- rep=repository/1
- repn=`pwd`/repository/
+ rep=pkg/1/build2.org
+ repn=build2.org/
repa=`pwd`/$rep
fi
@@ -104,6 +107,11 @@ function test ()
ops="-d $cfg"
fi
+ if [ "$cmd" = "cfg-fetch" -o \
+ "$cmd" = "rep-info" ]; then
+ ops="$ops --auth all"
+ fi
+
if [ -t 0 ]; then
$bpkg $cmd $ops $*
else
@@ -129,6 +137,11 @@ function fail ()
ops="-d $cfg"
fi
+ if [ "$cmd" = "cfg-fetch" -o \
+ "$cmd" = "rep-info" ]; then
+ ops="$ops --auth all"
+ fi
+
if [ "$verbose" = "y" ]; then
$bpkg $cmd $ops $*
else
@@ -164,6 +177,24 @@ function gone ()
#if false; then
+# Repository certificate fingerprint.
+#
+function rep_cert_fp ()
+{
+ cat $1/repositories | \
+ sed -n '/^-----BEGIN CERTIFICATE-----$/,/^-----END CERTIFICATE-----$/p' | \
+ openssl x509 -sha256 -noout -fingerprint | \
+ sed -n 's/^SHA256 Fingerprint=\(.*\)$/\1/p'
+}
+
+# Edit file with sed.
+#
+function edit ()
+{
+ local path=$1; shift
+ sed "$@" $path > $path.bak
+ mv $path.bak $path
+}
##
## Low-level commands.
@@ -175,25 +206,25 @@ function gone ()
##
fail pkg-verify # archive expected
fail pkg-verify ./no-such-file # archive does not exist
-fail pkg-verify repository/1/common/not-a-package.tar.gz
-fail pkg-verify --silent repository/1/common/not-a-package.tar.gz
-test pkg-verify repository/1/common/hello/libhello-1.0.0+1.tar.gz
+fail pkg-verify pkg/1/build2.org/common/not-a-package.tar.gz
+fail pkg-verify --silent pkg/1/build2.org/common/not-a-package.tar.gz
+test pkg-verify pkg/1/build2.org/common/hello/libhello-1.0.0+1.tar.gz
##
## rep-create
##
-fail rep-create # no 'repositories' file
-fail rep-create repository/1/satisfy # unexpected files
+fail rep-create # no 'repositories' file
+fail rep-create pkg/1/build2.org/satisfy # unexpected files
-test rep-create repository/1/common/hello
+test rep-create pkg/1/build2.org/common/hello --key key.pem
-test rep-create repository/1/common/foo/stable
-test rep-create repository/1/common/foo/testing
+test rep-create pkg/1/build2.org/common/foo/stable
+test rep-create pkg/1/build2.org/common/foo/testing
-test rep-create repository/1/common/bar/stable
-test rep-create repository/1/common/bar/testing
-test rep-create repository/1/common/bar/unstable
+test rep-create pkg/1/build2.org/common/bar/stable
+test rep-create pkg/1/build2.org/common/bar/testing
+test rep-create pkg/1/build2.org/common/bar/unstable
##
@@ -201,13 +232,13 @@ test rep-create repository/1/common/bar/unstable
##
fail rep-info # repository location expected
-test rep-info $rep/common/foo/testing <<EOF
+test rep-info --trust-yes $rep/common/foo/testing <<EOF
${repn}common/foo/testing $repa/common/foo/testing
complement ${repn}common/foo/stable $repa/common/foo/stable
libfoo 1.1.0
EOF
-test rep-info -m -r -n $rep/common/bar/unstable <<EOF
+test rep-info -m -r -n --trust-yes $rep/common/bar/unstable <<EOF
${repn}common/bar/unstable $repa/common/bar/unstable
: 1
location: ../../foo/testing
@@ -217,7 +248,7 @@ role: complement
:
EOF
-test rep-info -m -p $rep/common/bar/unstable <<EOF
+test rep-info -m -p --trust-yes $rep/common/bar/unstable <<EOF
: 1
sha256sum: 3034b727288efbb52b7b6e41fe147b815e7b3aa704e8cef6c2ee8d7421ab5b72
:
@@ -290,16 +321,17 @@ fail cfg-fetch # no repositories
# hello repository
#
+hello_fp=`rep_cert_fp pkg/1/build2.org/common/hello`
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
test cfg-fetch
# bar/unstable repository
#
test cfg-create --wipe
test cfg-add $rep/common/bar/unstable
-test cfg-fetch
+test cfg-fetch --trust-yes
test cfg-fetch
# both
@@ -307,14 +339,14 @@ test cfg-fetch
test cfg-create --wipe
test cfg-add $rep/common/hello
test cfg-add $rep/common/bar/unstable
-test cfg-fetch
+test cfg-fetch --trust-yes
test cfg-fetch
##
## pkg-fetch
##
-test rep-create repository/1/fetch/t1
+test rep-create pkg/1/build2.org/fetch/t1
test cfg-create --wipe
fail pkg-fetch -e # archive expected
@@ -327,28 +359,28 @@ fail pkg-fetch libfoo/1/2/3 # invalid package version
fail pkg-fetch libfoo/1.0.0 # no repositories
test cfg-add $rep/fetch/t1
fail pkg-fetch libfoo/1.0.0 # no packages
-test cfg-fetch
+test cfg-fetch --trust-yes
fail pkg-fetch libfoo/2+1.0.0 # not available
test cfg-create --wipe
test cfg-add $rep/fetch/t1
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-fetch libfoo/1.0.0
stat libfoo/1.0.0 fetched
fail pkg-fetch libfoo/1.0.0
-fail pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+fail pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
test pkg-purge libfoo
-test pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
stat libfoo/1.0.0 fetched
test pkg-unpack libfoo
test pkg-fetch -r libfoo/1.1.0
stat libfoo/1.1.0 fetched
test pkg-unpack libfoo
-test pkg-fetch -r -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -r -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
stat libfoo/1.0.0 fetched
test pkg-fetch -r libfoo/1.1.0
stat libfoo/1.1.0 fetched
-test pkg-fetch -r -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -r -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
stat libfoo/1.0.0 fetched
test pkg-purge libfoo
@@ -356,7 +388,7 @@ test pkg-purge libfoo
#
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
test pkg-fetch libhello/1.0.0+1
test pkg-purge libhello
@@ -370,37 +402,37 @@ fail pkg-unpack -e # package directory expected
fail pkg-unpack # package name expected
test cfg-add $rep/fetch/t1
-test cfg-fetch
+test cfg-fetch --trust-yes
# existing
#
fail pkg-unpack -e ./no-such-dir # package directory does not exist
-fail pkg-unpack -e ./repository # not a package directory
+fail pkg-unpack -e ./pkg # not a package directory
test pkg-fetch libfoo/1.0.0
-fail pkg-unpack -e repository/1/fetch/libfoo-1.1.0 # already exists
+fail pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0 # already exists
test pkg-purge libfoo
-test pkg-unpack -e repository/1/fetch/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
stat libfoo/1.1.0 unpacked
test pkg-purge libfoo
# existing & replace
#
test pkg-fetch libfoo/1.0.0
-fail pkg-unpack -e repository/1/fetch/libfoo-1.1.0
-test pkg-unpack -r -e repository/1/fetch/libfoo-1.1.0
+fail pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
+test pkg-unpack -r -e pkg/1/build2.org/fetch/libfoo-1.1.0
stat libfoo/1.1.0 unpacked
test pkg-purge libfoo
test pkg-fetch libfoo/1.0.0
test pkg-unpack libfoo
-fail pkg-unpack -e repository/1/fetch/libfoo-1.1.0
-test pkg-unpack -r -e repository/1/fetch/libfoo-1.1.0
+fail pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
+test pkg-unpack -r -e pkg/1/build2.org/fetch/libfoo-1.1.0
stat libfoo/1.1.0 unpacked
test pkg-purge libfoo
# package name
#
fail pkg-unpack libfoo # no such package in configuration
-test pkg-unpack -e repository/1/fetch/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
fail pkg-unpack libfoo # wrong package state
test pkg-purge libfoo
test pkg-fetch libfoo/1.0.0
@@ -413,7 +445,7 @@ test pkg-purge libfoo
#
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
test pkg-fetch libhello/1.0.0+1
test pkg-unpack libhello
test pkg-purge libhello
@@ -429,20 +461,20 @@ fail pkg-purge libfoo # no such package
# purge fetched
#
-test pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
test pkg-purge libfoo
stat libfoo unknown
# --keep
#
-test pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
test pkg-purge -k libfoo
stat libfoo "fetched 1.0.0"
test pkg-purge libfoo
# archive and --purge
#
-cp repository/1/fetch/libfoo-1.0.0.tar.gz $cfg/
+cp pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz $cfg/
test pkg-fetch -e -p $cfg/libfoo-1.0.0.tar.gz
test pkg-purge libfoo
stat libfoo unknown
@@ -450,20 +482,20 @@ gone $cfg/libfoo-1.0.0.tar.gz
# no archive but --keep
#
-test pkg-unpack -e repository/1/fetch/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
fail pkg-purge --keep libfoo
stat libfoo "unpacked 1.1.0"
test pkg-purge libfoo
# purge unpacked directory
#
-test pkg-unpack -e repository/1/fetch/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/fetch/libfoo-1.1.0
test pkg-purge libfoo
stat libfoo unknown
# purge unpacked archive
#
-test pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
test pkg-purge libfoo
stat libfoo unknown
@@ -471,7 +503,7 @@ gone $cfg/libfoo-1.0.0
# purge unpacked archive but --keep
#
-test pkg-fetch -e repository/1/fetch/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
test pkg-purge --keep libfoo
stat libfoo "fetched 1.0.0"
@@ -481,7 +513,7 @@ stat libfoo unknown
# directory and --purge
#
-cp -r repository/1/fetch/libfoo-1.1.0 $cfg/
+cp -r pkg/1/build2.org/fetch/libfoo-1.1.0 $cfg/
test pkg-unpack -e -p $cfg/libfoo-1.1.0
test pkg-purge libfoo
stat libfoo unknown
@@ -489,7 +521,7 @@ gone $cfg/libfoo-1.1.0
# archive and --purge
#
-cp repository/1/fetch/libfoo-1.0.0.tar.gz $cfg/
+cp pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz $cfg/
test pkg-fetch -e -p $cfg/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
test pkg-purge libfoo
@@ -499,7 +531,7 @@ gone $cfg/libfoo-1.0.0.tar.gz
# broken
#
-cp repository/1/fetch/libfoo-1.0.0.tar.gz $cfg/
+cp pkg/1/build2.org/fetch/libfoo-1.0.0.tar.gz $cfg/
test pkg-fetch -e -p $cfg/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
chmod 000 $cfg/libfoo-1.0.0
@@ -521,7 +553,7 @@ stat libfoo unknown
##
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
fail pkg-configure # package name expected
fail pkg-configure config.dist.root=/tmp # ditto
@@ -552,7 +584,7 @@ stat libhello/1.0.0 "available 1.0.0+1"
# src != out
#
test cfg-create --wipe
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
test pkg-configure libhello
stat libhello "configured 1.0.0+1"
test pkg-disfigure libhello
@@ -563,7 +595,7 @@ gone $cfg/libhello-1.0.0+1
# out still exists after disfigure
#
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
test pkg-configure libhello
touch $cfg/libhello-1.0.0+1/stray
fail pkg-disfigure libhello
@@ -574,7 +606,7 @@ stat libhello unknown
# disfigure failed
#
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
test pkg-configure libhello
chmod 555 $cfg/libhello-1.0.0+1
fail pkg-disfigure libhello
@@ -586,7 +618,7 @@ stat libhello unknown
# configure failed but disfigure succeeds
#
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
mkdir -p $cfg/libhello-1.0.0+1/build
chmod 555 $cfg/libhello-1.0.0+1/build
fail pkg-configure libhello
@@ -596,7 +628,7 @@ stat libhello unknown
# configure and disfigure both failed
#
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
mkdir -p $cfg/libhello-1.0.0+1/build
chmod 555 $cfg/libhello-1.0.0+1 $cfg/libhello-1.0.0+1/build # Trip both con/dis.
fail pkg-configure libhello
@@ -608,10 +640,10 @@ stat libhello unknown
# dependency management
#
-test rep-create repository/1/depend/stable
+test rep-create pkg/1/build2.org/depend/stable
test cfg-create --wipe
test cfg-add $rep/depend/stable
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-fetch libbar/1.0.0
test pkg-unpack libbar
@@ -677,10 +709,10 @@ test pkg-purge libbar
##
## pkg-status (also tested in pkg-{fetch,unpack,configure,disfigure,purge})
##
-test rep-create repository/1/status/stable
-test rep-create repository/1/status/extra
-test rep-create repository/1/status/testing
-test rep-create repository/1/status/unstable
+test rep-create pkg/1/build2.org/status/stable
+test rep-create pkg/1/build2.org/status/extra
+test rep-create pkg/1/build2.org/status/testing
+test rep-create pkg/1/build2.org/status/unstable
# basics
#
@@ -688,7 +720,7 @@ test cfg-create --wipe
stat libfoo/1.0.0 "unknown"
stat libfoo "unknown"
test cfg-add $rep/status/stable
-test cfg-fetch
+test cfg-fetch --trust-yes
stat libfoo/1.0.0 "available"
stat libfoo "available 1.0.0"
test pkg-fetch libfoo/1.0.0
@@ -699,20 +731,20 @@ stat libfoo "fetched 1.0.0"
#
test cfg-create --wipe
test cfg-add $rep/status/extra
-test cfg-fetch
+test cfg-fetch --trust-yes
stat libbar "available 1.1.0+1"
test cfg-add $rep/status/stable
-test cfg-fetch
+test cfg-fetch --trust-yes
stat libbar "available 1.1.0+1 1.0.0"
test cfg-create --wipe
test cfg-add $rep/status/testing
-test cfg-fetch
+test cfg-fetch --trust-yes
stat libbar "available 1.1.0 1.0.0+1 1.0.0"
test cfg-create --wipe
test cfg-add $rep/status/unstable
-test cfg-fetch
+test cfg-fetch --trust-yes
stat libbar "available 2.0.0 1.1.0 1.0.0+1 1.0.0"
test pkg-fetch libbar/1.0.0+1
stat libbar "fetched 1.0.0+1; available 2.0.0 1.1.0"
@@ -726,7 +758,7 @@ stat libbar "fetched 2.0.0"
##
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
fail pkg-update # package name expected
fail pkg-update libhello # no such package
@@ -747,7 +779,7 @@ test pkg-purge libhello
# src != out
#
test cfg-create --wipe
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
test pkg-configure libhello
test pkg-update libhello
test pkg-update libhello
@@ -760,7 +792,7 @@ test pkg-purge libhello
##
test cfg-create --wipe
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
fail pkg-clean # package name expected
fail pkg-clean libhello # no such package
@@ -782,7 +814,7 @@ test pkg-purge libhello
# src != out
#
test cfg-create --wipe
-test pkg-unpack -e repository/1/common/libhello-1.0.0+1
+test pkg-unpack -e pkg/1/build2.org/common/libhello-1.0.0+1
test pkg-configure libhello
test pkg-update libhello
test pkg-clean libhello
@@ -800,7 +832,7 @@ test pkg-purge libhello
#
test cfg-create --wipe cxx
test cfg-add $rep/common/hello
-test cfg-fetch
+test cfg-fetch --trust $hello_fp
test pkg-fetch libhello/1.0.0+1
test pkg-unpack libhello
test pkg-configure libhello
@@ -815,20 +847,20 @@ test pkg-purge libhello
# 1 (libfoo)
#
-test rep-create repository/1/satisfy/t1
+test rep-create pkg/1/build2.org/satisfy/t1
test cfg-create --wipe
fail pkg-build -p # package name expected
fail pkg-build -p libfoo # unknown package
fail pkg-build -p libfoo/1.0.0 # unknown package
-test pkg-build -p repository/1/satisfy/libfoo-1.1.0.tar.gz <<EOF
+test pkg-build -p pkg/1/build2.org/satisfy/libfoo-1.1.0.tar.gz <<EOF
build libfoo 1.1.0
EOF
-test pkg-build -p repository/1/satisfy/libfoo-1.1.0/ <<EOF
+test pkg-build -p pkg/1/build2.org/satisfy/libfoo-1.1.0/ <<EOF
build libfoo 1.1.0
EOF
-test pkg-unpack -e repository/1/satisfy/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/satisfy/libfoo-1.1.0
test pkg-build -p libfoo <<< "build libfoo 1.1.0"
test pkg-build -p libfoo/1.1.0 <<< "build libfoo 1.1.0"
test pkg-build -p libfoo libfoo <<< "build libfoo 1.1.0"
@@ -839,7 +871,7 @@ fail pkg-build -p libfoo/1.0.0
test pkg-purge libfoo
test cfg-add $rep/satisfy/t1
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -p libfoo <<< "build libfoo 1.0.0"
test pkg-build -p libfoo/1.0.0 <<< "build libfoo 1.0.0"
test pkg-build -p libfoo libfoo <<< "build libfoo 1.0.0"
@@ -848,13 +880,13 @@ test pkg-build -p libfoo/1.0.0 libfoo <<< "build libfoo 1.0.0"
test pkg-build -p libfoo/1.0.0 libfoo/1.0.0 <<< "build libfoo 1.0.0"
fail pkg-build -p libfoo/1.1.0
-test pkg-unpack -e repository/1/satisfy/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/satisfy/libfoo-1.1.0
test pkg-build -p libfoo <<< "build libfoo 1.1.0"
test pkg-build -p libfoo/1.0.0 <<< "downgrade libfoo 1.0.0"
fail pkg-build -p libfoo/0.0.0
test pkg-purge libfoo
-test pkg-fetch -e repository/1/satisfy/libfoo-0.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz
test pkg-unpack libfoo
test pkg-build -p libfoo <<< "upgrade libfoo 1.0.0"
test pkg-build -p libfoo/0.0.0 <<< "build libfoo 0.0.0"
@@ -863,13 +895,13 @@ test pkg-purge libfoo
# 2 (libbar depends on libfoo)
#
-test rep-create repository/1/satisfy/t2
+test rep-create pkg/1/build2.org/satisfy/t2
test cfg-create --wipe
-fail pkg-build repository/1/satisfy/libbar-1.0.0.tar.gz
+fail pkg-build pkg/1/build2.org/satisfy/libbar-1.0.0.tar.gz
test cfg-add $rep/satisfy/t2
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -p libbar <<EOF
build libfoo 1.0.0 (required by libbar)
@@ -889,7 +921,7 @@ build libbar 1.0.0
EOF
fail pkg-build -p libbar libfoo/1.1.0
-test pkg-fetch -e repository/1/satisfy/libfoo-0.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz
test pkg-unpack libfoo
test pkg-build -p libbar <<EOF
build libfoo 0.0.0 (required by libbar)
@@ -905,7 +937,7 @@ build libbar 1.0.0
EOF
test pkg-purge libfoo
-test pkg-unpack -e repository/1/satisfy/libfoo-1.1.0
+test pkg-unpack -e pkg/1/build2.org/satisfy/libfoo-1.1.0
test pkg-build -p libbar <<EOF
build libfoo 1.1.0 (required by libbar)
build libbar 1.0.0
@@ -922,10 +954,10 @@ test pkg-purge libfoo
# 3 (libbaz depends on libbar; libbar in prerequisite repository)
#
-test rep-create repository/1/satisfy/t3
+test rep-create pkg/1/build2.org/satisfy/t3
test cfg-create --wipe
test cfg-add $rep/satisfy/t3
-test cfg-fetch
+test cfg-fetch --trust-yes
# only in prerequisite repository
#
@@ -1038,14 +1070,14 @@ EOF
# 4 (libbaz depends on libfoo and libbar; libbar depends on libfoo >= 1.1.0)
#
-test rep-create repository/1/satisfy/t4a
-test rep-create repository/1/satisfy/t4b
-test rep-create repository/1/satisfy/t4c
-test rep-create repository/1/satisfy/t4d
+test rep-create pkg/1/build2.org/satisfy/t4a
+test rep-create pkg/1/build2.org/satisfy/t4b
+test rep-create pkg/1/build2.org/satisfy/t4c
+test rep-create pkg/1/build2.org/satisfy/t4d
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -p libbaz <<EOF
build libfoo 1.1.0 (required by libbar libbaz)
@@ -1064,7 +1096,7 @@ fail pkg-build -p libfoo/1.1.0 libbaz
# upgrade warning
#
-test pkg-fetch -e repository/1/satisfy/libfoo-0.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-0.0.0.tar.gz
test pkg-unpack libfoo
test pkg-build -p libbaz <<EOF
upgrade libfoo 1.1.0 (required by libbar libbaz)
@@ -1075,11 +1107,11 @@ test pkg-purge libfoo
# downgrade error
#
-test pkg-fetch -e repository/1/satisfy/libfoo-1.2.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-1.2.0.tar.gz
test pkg-unpack libfoo
fail pkg-build -p libbaz
test cfg-add $rep/satisfy/t4a
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -p libfoo/1.1.0 libbaz <<EOF
downgrade libfoo 1.1.0
build libbar 1.1.0 (required by libbaz)
@@ -1095,7 +1127,7 @@ test pkg-configure libfoo
test pkg-fetch libbar/1.1.0
test pkg-unpack libbar
test pkg-configure libbar
-fail pkg-build -p repository/1/satisfy/libfoo-1.2.0.tar.gz
+fail pkg-build -p pkg/1/build2.org/satisfy/libfoo-1.2.0.tar.gz
fail pkg-build -p libfoo/1.0.0
test pkg-build -p libfoo/1.1.0 <<< "build libfoo 1.1.0"
test pkg-disfigure libbar
@@ -1107,19 +1139,19 @@ test pkg-purge libfoo
#
test cfg-create --wipe
-test pkg-fetch -e repository/1/satisfy/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
test pkg-configure libfoo
-test pkg-fetch -e repository/1/satisfy/libbar-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libbar-1.0.0.tar.gz
test pkg-unpack libbar
test pkg-configure libbar
-test pkg-fetch -e repository/1/satisfy/libbaz-1.1.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libbaz-1.1.0.tar.gz
test pkg-unpack libbaz
test pkg-configure libbaz
test cfg-add $rep/satisfy/t4a
test cfg-add $rep/satisfy/t4b
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -p libbar <<EOF
upgrade libfoo 1.1.0 (required by libbar libbaz)
@@ -1160,7 +1192,7 @@ EOF
#
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbaz
stat libfoo/1.1.0 "configured"
stat libbar/1.1.0 "configured"
@@ -1169,14 +1201,14 @@ stat libbaz/1.1.0 "configured hold_package"
# hold
#
test cfg-create --wipe
-test pkg-build -y repository/1/satisfy/libfoo-1.0.0.tar.gz
+test pkg-build -y pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz
stat libfoo "configured 1.0.0 hold_package hold_version"
-test pkg-build -y repository/1/satisfy/libfoo-1.1.0/
+test pkg-build -y pkg/1/build2.org/satisfy/libfoo-1.1.0/
stat libfoo "configured 1.1.0 hold_package hold_version"
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libfoo
stat libfoo "configured 1.0.0 hold_package"
test pkg-build -y libfoo/1.0.0
@@ -1184,12 +1216,12 @@ stat libfoo "configured 1.0.0 hold_package hold_version"
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libfoo/1.0.0
stat libfoo "configured 1.0.0 hold_package hold_version"
test cfg-create --wipe
-test pkg-fetch -e repository/1/satisfy/libfoo-1.0.0.tar.gz
+test pkg-fetch -e pkg/1/build2.org/satisfy/libfoo-1.0.0.tar.gz
test pkg-unpack libfoo
test pkg-configure libfoo
stat libfoo "configured 1.0.0"
@@ -1198,7 +1230,7 @@ stat libfoo "configured 1.0.0 hold_package"
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libfoo
stat libfoo "configured 1.0.0 hold_package"
test pkg-build -y libbaz
@@ -1206,30 +1238,30 @@ stat libfoo "configured 1.1.0 hold_package"
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libfoo/1.0.0
stat libfoo "configured 1.0.0 hold_package hold_version"
fail pkg-build -y libbaz
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbaz
stat libfoo "configured 1.1.0"
# drop prerequisites on downgrade
#
-test rep-create repository/1/satisfy/t5
+test rep-create pkg/1/build2.org/satisfy/t5
test cfg-create --wipe
test cfg-add $rep/satisfy/t2
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbar
stat libfoo "configured 1.0.0"
stat libbar "configured 1.0.0 hold_package"
test cfg-add $rep/satisfy/t5
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbar
stat libfoo "available 1.0.0"
@@ -1254,7 +1286,7 @@ fail pkg-drop -p libfoo/1.0.0 # unknown package
test cfg-create --wipe
test cfg-add $rep/satisfy/t4c
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbaz
test pkg-drop -p -y libfoo libbaz libbar <<EOF
@@ -1319,7 +1351,7 @@ EOF
test cfg-create --wipe
test cfg-add $rep/satisfy/t4d
-test cfg-fetch
+test cfg-fetch --trust-yes
test pkg-build -y libbiz
test pkg-drop -p -y libbiz <<EOF
@@ -1404,3 +1436,141 @@ stat libfoo/1.1.0 "unknown"
stat libbar/1.1.0 "unknown"
stat libbaz/1.1.0 "unknown"
stat libbiz/1.0.0 "available"
+
+##
+## auth
+##
+
+# rep-create
+#
+fail rep-create pkg/1/build2.org/auth/create-noemail --key key.pem
+fail rep-create pkg/1/build2.org/auth/create-expired --key key.pem
+
+fail rep-create pkg/1/build2.org/auth/signed # no --key option
+test rep-create pkg/1/build2.org/auth/signed --key key.pem
+test rep-create pkg/1/build2.org/auth/unsigned1
+test rep-create pkg/1/build2.org/auth/unsigned2
+test rep-create pkg/1/build2.org/auth/name-mismatch --key key.pem
+
+test rep-create pkg/1/build2.org/auth/sha256sum-mismatch --key key.pem
+
+# Tamper signature manifest's sha256sum value.
+#
+s=d374c59b36fdbdbd0d4468665061d94fda9c6c687863dfe72b0bcc34ff9d5fb4
+edit pkg/1/build2.org/auth/sha256sum-mismatch/signature \
+ "s/^\(sha256sum: \).*\$/\1$s/"
+
+test rep-create pkg/1/build2.org/auth/signature-mismatch --key key.pem
+
+# Tamper signature manifest's signature value.
+#
+edit pkg/1/build2.org/auth/signature-mismatch/signature \
+ '/^signature: \\$/,/^\\$/d'
+cat >> pkg/1/build2.org/auth/signature-mismatch/signature << EOF
+signature: \\
+XBjnmXXVHY0RqMI0gL/P4t/vuWwK9JJkLl4Qf2gMxq5k2WQ2CIE56DfG0RaGklgKcI3UxsQZvMQI
+5PNtAHJDjteQ+BqY0io8A43KPX+2LKMU+I825sKmPRjCLYleGM3mNndDkWfYtAzYk5AmR2piqRz0
+D7CLq9GIoQQZO4Fw44muaQDMCRcXy8Txx2jDnretQjx/C0ZQw4M/cd6/cKEKUmLITDkBig9oVlSh
+tpxHqWz5NTbO3vm8ILc03AwiOJHwZweLb6ocJ6a467IJa+F/xUm9B09k8wFWMs+jHXXzHDE0syv7
+lqWL7SvHSjVFrGVFKS6nx7lCj2b8XFiGlwWIwjY4m/VK/5QmbL/lC4f+ww5XT5NG5iYh/eMaCxCJ
+zTg5iZsWNLhrx9uKNrL5xC4z0OONRVOwzu7gsqr0GLWewPyhH0AqJLgOSkw9N7FJwbv2IKNZ88YA
+u2YMXNkXytcQvENLVQDX5oxvUMEurUJFOCuYB/SEnpcwkV5h9RtXzIFVy4OCTU2MhQHDEldI8s7w
+Hga/ct4WupgE228gGdgwJLCbHx6AWBlS9iL10AdS8JkQ9LaZwTMHHz44f8y00X4MiT06gpgDeoQD
+rUyP0KNG65tdWnVTMqg6Q/YXhtRZLHoD6+QbiYLlruR1phu4y4fDt7AKxoXfeme/a86A37UogZY=
+\\
+EOF
+
+# cfg-fetch
+#
+test cfg-create --wipe
+test cfg-add $rep/auth/signed
+test cfg-fetch --trust `rep_cert_fp pkg/1/build2.org/auth/signed`
+test cfg-fetch
+test cfg-fetch --trust-no # certificate is already trusted
+
+test cfg-create --wipe
+test cfg-add $rep/auth/signed
+test cfg-fetch --trust-yes
+test cfg-fetch
+
+test cfg-create --wipe
+test cfg-add $rep/auth/signed
+fail cfg-fetch --trust-no
+
+test cfg-create --wipe
+test cfg-add $rep/auth/signed
+fail cfg-fetch --trust-yes --trust-no # inconsistent options
+
+test cfg-create --wipe
+test cfg-add $rep/auth/unsigned1
+test cfg-fetch --trust-yes
+test cfg-fetch
+test cfg-add $rep/auth/unsigned2
+test cfg-fetch
+test cfg-fetch --trust-no # certificates are already trusted
+
+test cfg-create --wipe
+test cfg-add $rep/auth/unsigned1
+fail cfg-fetch --trust-no
+
+test cfg-create --wipe
+test cfg-add $rep/auth/name-mismatch
+fail cfg-fetch --trust-yes # certificate name mismatch
+
+test cfg-create --wipe
+test cfg-add $rep/auth/expired
+fail cfg-fetch --trust-yes # certificate expired
+
+test cfg-create --wipe
+test cfg-add $rep/auth/sha256sum-mismatch
+fail cfg-fetch --trust-yes # packages file checksum mismatch
+
+test cfg-create --wipe
+test cfg-add $rep/auth/signature-mismatch
+fail cfg-fetch --trust-yes # packages file signature:mismatch
+
+# rep-info
+#
+test cfg-create --wipe
+test rep-info --trust-yes -d $cfg $rep/auth/signed <<EOF
+${repn}auth/signed $repa/auth/signed
+libfoo 1.0.0
+EOF
+
+test rep-info -d $cfg $rep/auth/signed <<EOF
+${repn}auth/signed $repa/auth/signed
+libfoo 1.0.0
+EOF
+
+test cfg-create --wipe
+test rep-info --trust-yes $rep/auth/signed <<EOF
+${repn}auth/signed $repa/auth/signed
+libfoo 1.0.0
+EOF
+
+fail rep-info $rep/auth/signed <<EOF
+${repn}auth/signed $repa/auth/signed
+libfoo 1.0.0
+EOF
+
+test cfg-create --wipe
+test rep-info --trust-yes -d $cfg $rep/auth/unsigned1 <<EOF
+${repn}auth/unsigned1 $repa/auth/unsigned1
+libfoo 1.0.0
+EOF
+
+test rep-info -d $cfg $rep/auth/unsigned2 <<EOF
+${repn}auth/unsigned2 $repa/auth/unsigned2
+libfoo 1.0.0
+EOF
+
+test cfg-create --wipe
+test rep-info --trust-yes $rep/auth/unsigned1 <<EOF
+${repn}auth/unsigned1 $repa/auth/unsigned1
+libfoo 1.0.0
+EOF
+
+fail rep-info $rep/auth/unsigned1 <<EOF
+${repn}auth/unsigned1 $repa/auth/unsigned1
+libfoo 1.0.0
+EOF