What does it take to “identify software”? How can we tell what softwareis running on a machine to determine, for example, what securityvulnerabilities might affect it?
In October 2023, the US Cybersecurity and Infrastructure Security Agency(CISA) published a white paper entitled Software IdentificationEcosystem OptionAnalysisthat looks at existing options to address these questions. Thepublication was followed by a request forcomments; ourcommentas Guix developers didn’t make it on time to be published, but we’d liketo share it here.
Software identification for cybersecurity purposes is a crucial topic,as the white paper explains in its introduction:
Effective vulnerability management requires software to be trackablein a way that allows correlation with other information such as knownvulnerabilities […]. This correlation is only possible when differentcybersecurity professionals know they are talking about the samesoftware.
The Common Platform Enumeration(CPE)standard has been designed to fill that role; it is used to identifysoftware as part of the well-known Common Vulnerabilities and Exposures(CVE)process. But CPE is showing its limits as an extrinsic identificationmechanism: the human-readable identifiers chosen by CPE fail to capturethe complexity of what “software” is.
We think functional software deployment as implemented by Nix and Guix,coupled with the source code identification work carried out by SoftwareHeritage, provides a unique perspective on these matters.
On Software IdentificationThe Software Identification Ecosystem Option Analysis white paperreleased by CISA in October 2023 studies options towards the definitionof a software identification ecosystem that can be used across thecomplete, global software space for all key cybersecurity use cases.
Our experience lies in the design and development ofGNU Guix, a package manager, software deploymenttool, and GNU/Linux distribution, which emphasizes three key elements:reproducibility, provenance tracking, and auditability. We explainin the following sections our approach and how it relates to the goalstated in the aforementioned white paper.
Guix produces binary artifacts of varying complexity from source code:package binaries, application bundles (container images to be consumedby Docker and related tools), system installations, system bundles(container and virtual machine images).
All these artifacts qualify as “software” and so does source code. Someof this “software” comes from well-identified upstream packages,sometimes with modifications added downstream by packagers (patches);binary artifacts themselves are the byproduct of a build process wherethe package manager uses other binary artifacts it previously built(compilers, libraries, etc.) along with more source code (the packagedefinition) to build them. How can one identify “software” in thatsense?
Software is dual: it exists in source form and in binary,machine-executable form. The latter is the outcome of a complexcomputational process taking source code and intermediary binaries asinput.
Our thesis can be summarized as follows:
We consider that the requirements for source code identifiers differfrom the requirements to identify binary artifacts.
Our view, embodied in GNU Guix, is that:
- Source code can be identified in an unambiguous anddistributed fashion through inherent identifiers such ascryptographic hashes.
- Binary artifacts, instead, need to be the byproduct of acomprehensive and verifiable build process itself available assource code.
In the next sections, to clarify the context of this statement, we showhow Guix identifies source code, how it defines the source-to-binarypath and ensures its verifiability, and how it provides provenancetracking.
Source Code IdentificationGuix includes packagedefinitionsfor almost 30,000 packages. Each package definition identifies itsorigin—its“main” source code as well as patches. The origin iscontent-addressed: it includes a SHA256 cryptographic hash of thecode (an inherent identifier), along with a primary URL to downloadit.
Since source is content-addressed, the URL can be thought of as a hint.Indeed, we connected Guix to the SoftwareHeritage source code archive: whensource code vanishes from its original URL, Guix falls back todownloading it from the archive. This is made possible thanks to the useof inherent (or intrinsic) identifiers both by Guix and SoftwareHeritage.
More information can be found in this 2019 blogpostand in the documents of the Software Hash Identifiers(SWHID) working group.
Reproducible BuildsGuix provides a verifiable path from source code to binaries byensuring reproducible builds. Toachieve that, Guix builds upon the pioneering research work of EelcoDolstra that led to the design of the Nix packagemanager, with which it shares the same conceptualfoundation.
Namely, Guix relies on hermetic builds: builds are performed inisolated environments that contain nothing but explicitly-declareddependencies—where a “dependency” can be the output of another buildprocess or source code, including build scripts and patches.
An implication is that builds can be verified independently. Forinstance, for a given version of Guix, guix build gccshould produce the exact same binary, bit-for-bit. To facilitateindependent verification, guix challenge gcc compares thebinary artifacts of the GNU Compiler Collection (GCC) as built andpublished by different parties. Users can also compare to a local buildwith guix build gcc --check.
As with Nix, build processes are identified by derivations, which arelow-level, content-addressed build instructions; derivations may referto other derivations and to source code. For instance,/gnu/store/c9fqrmabz5nrm2arqqg4ha8jzmv0kc2f-gcc-11.3.0.drvuniquely identifies the derivation to build a specific variant ofversion 11.3.0 of the GNU Compiler Collection (GCC). Changing thepackage definition—patches being applied, build flags, set ofdependencies—, or similarly changing one of the packages it dependson, leads to a different derivation (more information can be found inEelco Dolstra's PhDthesis).
Derivations form a graph that captures the entirety of the buildprocesses leading to a binary artifact. In contrast, mere packagename/version pairs such as gcc 11.3.0 fail to capture thebreadth and depth elements that lead to a binary artifact. This is ashortcoming of systems such as the Common Platform Enumeration (CPE)standard: it fails to express whether a vulnerability that applies togcc 11.3.0 applies to it regardless of how it was built,patched, and configured, or whether certain conditions are required.
Full-Source BootstrapReproducible builds alone cannot ensure the source-to-binarycorrespondence: the compiler could contain a backdoor, as demonstratedby Ken Thompson in Reflections on Trusting Trust. To address that,Guix goes further by implementing so-called full-source bootstrap:for the first time, literally every package in the distribution is builtfrom source code, starting from a very small binaryseed.This gives an unprecedented level of transparency, allowing code to beaudited at all levels, and improving robustness against the“trusting-trust attack” described by Ken Thompson.
The European Union recognized the importance of this work through anNLnet Privacy & Trust Enhancing Technologies (NGI0 PET)grant allocated in2021 to Jan Nieuwenhuizen to further work on full-source bootstrap inGNU Guix, GNU Mes, and related projects, followed by anothergrant in 2022 to expandsupport to the Arm and RISC-V CPU architectures.
Provenance TrackingWe define provenance tracking as the ability to map a binary artifactback to its complete corresponding source. Provenance tracking isnecessary to allow the recipient of a binary artifact to access thecorresponding source code and to verify the source/binary correspondenceif they wish to do so.
Theguix packcommand can be used to build, for instance, containers images. Runningguix pack -f docker python --save-provenance produces aself-describing Docker image containing the binaries of Python and itsrun-time dependencies. The image is self-describing because--save-provenance flag leads to the inclusion of amanifest that describes which revision of Guix was used to producethis binary. A third party can retrieve this revision of Guix and fromthere view the entire build dependency graph of Python, view its sourcecode and any patches that were applied, and recursively for itsdependencies.
To summarize, capturing the revision of Guix that was used is all ittakes to reproduce a specific binary artifact. This is illustrated bythe time-machinecommand.The example below deploys, at any time on any machine, the specificbuild artifact of the python package as it was defined in this Guixcommit:
guix time-machine -q --commit=d3c3922a8f5d50855165941e19a204d32469006f \ -- install python
In other words, because Guix itself defines how artifacts are built,the revision of the Guix source coupled with the package nameunambiguously identify the package’s binary artifact. Asscientists, we build on this property to achieve reproducible researchworkflows, as explained in this 2022 article in Nature ScientificData; as engineers, wevalue this property to analyze the systems we are running and determinewhich known vulnerabilities and bugs apply.
Again, a software bill of materials (SBOM) written as a mere list ofpackage name/version pairs would fail to capture as much information.The Artifact Dependency Graph (ADG) ofOmniBOR, while less ambiguous, falls short intwo ways: it is too fine-grained for typical cybersecurity applications(at the level of individual source files), and it only captures thealleged source/binary correspondence of individual files but not theprocess to go from source to binary.
ConclusionsInherent identifiers lend themselves well to unambiguous source codeidentification, as demonstrated by Software Heritage, Guix, and Nix.
However, we believe binary artifacts should instead be treated as theresult of a computational process; it is that process that needs to befully captured to support independent verification of thesource/binary correspondence. For cybersecurity purposes, recipientsof a binary artifact must be able to be map it back to its source code(provenance tracking), with the additional guarantee that they must beable to reproduce the entire build process to verify the source/binarycorrespondence (reproducible builds and full-source bootstrap). Aslong as binary artifacts result from a reproducible build process,itself described as source code, identifying binary artifacts boilsdown to identifying the source code of their build process.
These ideas are developed in the 2022 scientific paper Building aSecure Software Supply Chain withGNU Guix