From f23cd3159444361233b257ab8341b260b378064e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 29 Jan 2016 12:16:09 +0200 Subject: Add support for checksum verification --- bpkg/manifest | 6 +++ bpkg/manifest.cxx | 112 ++++++++++++++++++++++++++++++++++++++++++++---- tests/manifest/packages | 5 +++ 3 files changed, 114 insertions(+), 9 deletions(-) diff --git a/bpkg/manifest b/bpkg/manifest index 6bfe8e7..d932b2a 100644 --- a/bpkg/manifest +++ b/bpkg/manifest @@ -330,6 +330,7 @@ namespace bpkg // The following values are only valid in the manifest list. // butl::optional location; + butl::optional sha256sum; public: package_manifest (manifest_parser&, bool ignore_unknown = false); @@ -348,6 +349,11 @@ namespace bpkg using base_type::base_type; + // Checksum of the corresponding repository_manifests. + // + std::string sha256sum; + + public: package_manifests () = default; package_manifests (manifest_parser&, bool ignore_unknown = false); diff --git a/bpkg/manifest.cxx b/bpkg/manifest.cxx index b6da488..0923a48 100644 --- a/bpkg/manifest.cxx +++ b/bpkg/manifest.cxx @@ -59,6 +59,21 @@ namespace bpkg return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } + inline static bool + valid_sha256 (const string& s) noexcept + { + if (s.size () != 64) + return false; + + for (const auto& c: s) + { + if ((c < 'a' || c > 'f' ) && !digit (c)) + return false; + } + + return true; + } + // Replace std::tolower to keep things locale independent. // inline static char @@ -1064,6 +1079,16 @@ namespace bpkg bad_value ("invalid package location"); } } + else if (n == "sha256sum") + { + if (sha256sum) + bad_name ("package sha256sum redefinition"); + + if (!valid_sha256 (v)) + bad_value ("invalid package sha256sum"); + + sha256sum = move (v); + } else if (!iu) bad_name ("unknown name '" + n + "' in package manifest"); } @@ -1088,7 +1113,8 @@ namespace bpkg serialize (serializer& s) const { // @@ Should we check that all non-optional values are specified ? - // @@ Should we check the version release is not empty ? + // @@ Should we check that values are valid: name is not empty, version + // release is not empty, sha256sum is a proper string, ... // s.next ("", "1"); // Start of manifest. @@ -1151,6 +1177,9 @@ namespace bpkg if (location) s.next ("location", location->posix_string ()); + if (sha256sum) + s.next ("sha256sum", *sha256sum); + s.next ("", ""); // End of manifest. } @@ -1160,27 +1189,92 @@ namespace bpkg package_manifests (parser& p, bool iu) { name_value nv (p.next ()); - while (!nv.empty ()) + + auto bad_name ([&p, &nv](const string& d) { + throw parsing (p.name (), nv.name_line, nv.name_column, d);}); + + auto bad_value ([&p, &nv](const string& d) { + throw parsing (p.name (), nv.value_line, nv.value_column, d);}); + + // Make sure this is the start and we support the version. + // + if (!nv.name.empty ()) + bad_name ("start of package list manifest expected"); + + if (nv.value != "1") + bad_value ("unsupported format version"); + + // Parse the package list manifest. + // + for (nv = p.next (); !nv.empty (); nv = p.next ()) + { + string& n (nv.name); + string& v (nv.value); + + if (n == "sha256sum") + { + if (!sha256sum.empty ()) + bad_name ("sha256sum redefinition"); + + if (!valid_sha256 (v)) + bad_value ("invalid sha256sum"); + + sha256sum = move (v); + } + else if (!iu) + bad_name ("unknown name '" + n + "' in package list manifest"); + } + + // Verify all non-optional values were specified. + // + if (sha256sum.empty ()) + bad_value ("no sha256sum specified"); + + // Parse package manifests. + // + for (nv = p.next (); !nv.empty (); ) { push_back (package_manifest (p, nv, iu)); nv = p.next (); - if (!back ().location) - throw parsing (p.name (), nv.name_line, nv.name_column, - "package location expected"); + const package_manifest& m (back ()); + + if (!m.location) + bad_name ("package location expected"); + + if (!m.sha256sum) + bad_name ("package sha256sum expected"); } } void package_manifests:: serialize (serializer& s) const { + // Serialize the package list manifest. + // + // @@ Should we check that values are valid ? + // + s.next ("", "1"); // Start of manifest. + s.next ("sha256sum", sha256sum); + s.next ("", ""); // End of manifest. + + // Serialize package manifests. + // for (const package_manifest& p: *this) { - if (!p.location || p.location->empty ()) - throw + auto no_value = [&p, &s](const string& d) + { + throw serialization ( s.name (), - "no valid location for " + p.name + "-" + p.version.string ()); + d + " for " + p.name + "-" + p.version.string ()); + }; + + if (!p.location) + no_value ("no valid location"); + + if (!p.sha256sum) + no_value ("no valid sha256sum"); p.serialize (s); } @@ -1779,7 +1873,7 @@ namespace bpkg static const char* invalid_url ("invalid relative url"); - auto strip([&i, &rp]() -> bool { + auto strip ([&i, &rp]() -> bool { if (i != rp.end ()) { const auto& c (*i++); diff --git a/tests/manifest/packages b/tests/manifest/packages index d204c32..dda9392 100644 --- a/tests/manifest/packages +++ b/tests/manifest/packages @@ -1,4 +1,6 @@ : 1 +sha256sum: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +: name: libfoo version: 1.2.3+2 priority: high; Due to critical bug fix. @@ -27,6 +29,7 @@ requires: ? ; libc++ standard library if using Clang on Mac OS X. requires: zlib; Most Linux/UNIX systems already have one; or get it at\ www.zlib.net. location: libfoo-1.2.3+2.tar.bz2 +sha256sum: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 : name: libbar version: 3.4A.5+6 @@ -38,6 +41,7 @@ url: http://www.example.org/projects/libbar/ email: libbar-users@example.org depends: libbaz (1- 2-) | libbaz [3 4-) | libbaz (5 6] | libbaz [7 8] location: bar/libbar-3.4A.5+6.tbz +sha256sum: d4b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 : name: libbaz version: 2~3.4A.5+3 @@ -46,3 +50,4 @@ license: LGPLv2 url: http://www.example.org/projects/libbar/ email: libbaz-users@example.org location: libbaz/libbaz-2~3.4A.5+3.tar.gz +sha256sum: b5b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -- cgit v1.1