Rust makes DNS and time software more secure
Vital software for critical internet infrastructure reimplemented
Vital software for critical internet infrastructure reimplemented
Essential software for critical internet infrastructure is currently being rewritten in the Rust programming language. The main feature of this relatively new language is that it offers developers built-in memory safety without performance loss (by means of zero-cost abstractions).
It's often said that roughly 70 per cent of all serious vulnerabilities in system software are attributable to bugs linked to a lack of memory safety. If so, it follows that maybe more than half of all security issues with system and network software could be prevented by switching from the C and C++ programming languages to Rust. Developers are therefore working on a variety of projects, including (re)implementations of software for DNS, time synchronisation and TLS. The latter 2 types in particular are directly relevant to our services.
The Rust programming language is quickly gaining ground in the world of system and network programming. So much so that Rust is now the default language for new implementations at NLnet Labs, one of the world's leading developers of software for the internet's core infrastructure. The NLnet Labs products now written in Rust include all of the team's RPKI software – a relatively new pilar alongside their DNS-software.
Originally, the Domain crate (a Rust library for DNS) [@GitHub] served as a proof-of-concept for Rust programming. Now, however, the library forms the foundation for a Rust-based DNS suite, which will soon include a validating resolver, an authoritative name server, a DNS proxy and a feature-rich diagnostic tool. The functionality of the existing ldns library and tools (written in C and now merely maintained) will be incorporated into the Domain crate.
Development of the Domain suite was enabled by a grant of nearly 1 million euros from the Sovereign Tech Fund, a German federal government funding body that supports the development of open-source internet software.
Rust is a relatively new programming language [book, @YouTube, Getting Started with Rust, Comprehensive Rust]. It was developed at Mozilla and initially used to write a new browser engine called Servo. The language has since been made open source and responsibility for its upkeep passed to the Rust Foundation, established in 2021 by Amazon, Huawei, Google, Microsoft and Mozilla.
Rust looks very similar to C and C++, still the classic programming languages for system and network software. However, Rust has the crucial added value of being memory safe. In other words, access to data structures is controlled, so that the programmer doesn't have unfettered access to whatever memory addresses they choose. By contrast, C and C++ give the programmer essentially free access to the entire memory by using pointers and indexes. In fact, the interchangeability of indexes and pointers is a standard feature in C.
For readers who are familiar with C/C++, we have summarised the main features of memory-safe Rust and how it differs from C at the end of this article.
It's often said that roughly 70 per cent of all serious vulnerabilities in system software are attributable to bugs linked to a lack of memory safety [Microsoft, Android, Apple, C++]. The best-known example is the Heartbleed bug, involving a buffer over-read in the popular OpenSSL library, which rendered many TLS connections insecure 10 years ago.
To give an impression: in 2023, nearly 29,000 CVEs were registered, of which more than half were designated 'serious' (a CVSS score of 7.0 or higher).[1]
The high proportion of software vulnerabilities attributable to lack of memory safety led to the FBI, the NSA [1] and a large number of cybersecurity centres calling for the use of memory-safe programming languages. Promotion of such languages was a feature of the CISA's Secure by Design project [1, 2], an international campaign to make software more secure, whose participants included the Dutch National Cyber Security Centre. According to the recommendation's authors, the use of memory-safe programming languages could eliminate an entire category of vulnerability.
As well as the independent Rust and Python languages – the latter developed by the CWI, the SIDN's predecessor as operator of .nl – the recommendation mentions several vendor-specific languages as memory safe, including C#, Go, Java and Swift. The NSA recently added Ruby, Ada and Delphi to the list [1]. However, Rust is the only language that can reasonably serve as a replacement for C in generic system and network programming.
Software developers and firms are advised to draw up a roadmap towards memory-safe programming languages (and to communicate it to their customers and users). The recommendation's authors also indicate what a suitable approach would involve. However, they acknowledge that the transition will take some years and require significant investment, because of the huge installed base.
The main reason for making the switch is to increase the security of the program code, but it is also expected to reduce expenditure on, for example, training, tools, use of best practices (for coding, segmentation and architecture), use of security options in hardware, operating systems and compilers, (automated) testing, (automated) code analysis, audits, security updates, patch management and incident response. The authors anticipate that some of the required investment will ultimately be recovered [1] through the production of software that is more secure, more reliable and better quality.
The CISA recommendation is backed up by the US presidency's Office of the National Cyber Director (the ONCD, which is responsible for national cybersecurity strategy), which believes that responsibility for the transition to memory-safe technology lies primarily with the vendors. In its report Back to the Building Blocks: A Path Toward Secure and Measurable Software, the ONCD sets out a two-part strategy for reducing vulnerabilities:
Reduction of the (online) attack surface by eliminating an entire category of vulnerabilities, with the use of memory-safe programming languages wherever possible as the key
Development of better diagnostic tools for measuring system security, involving the development of security-related metrics and tools as contributors to software quality
Where the first part of the strategy is concerned, software and hardware vendors are encouraged to adopt memory-safe languages. The second part of the strategy is a task for researchers working on software (quality) metrics, software architecture and security.
The ONCD's recommendation also prompted a response from Bjarne Stroustrup, the inventor of C++. He argues that modern C++ provides sufficient security guarantees, but agrees that better development tools and processes are required. [1] However, the general consensus in the technical community is that Stroustrop is fighting a losing battle to save his brainchild. [1, 2]
The inherent robustness of Rust code is one of the main reasons for the language's growth. That's the belief of Erik Jonkers, event organiser at the Rust Foundation of the Netherlands (RustNL) and Open Source Director at the Tweede Golf enterprise. The latest annual Stack Overflow Developer Survey puts Rust 14th in the list of the most used languages. Notably, however, the survey has for nearly a decade ranked Rust the most popular language. The TIOBE Index now has Rust in 18th place.
Figure 1: Rust has been the most popular language for nearly a decade: 85 per cent of current programmers want to continue using the language. [Source: Stack Overflow Developer Survey 2023] View this chart in tabular form
Figure 2: Rust is 'only' the 14th most used language. [Source: Stack Overflow Developer Survey 2023]
According to Jonkers, the big gap between Rust's popularity and its use is due to the language being best suited to system and network programming: a relatively small, specialist field. In the much larger web development sector, for example, PHP, JavaScript and various related frameworks dominate.
"At Tweede Golf, we started using Rust about 7 years ago, mainly because it's a good language that all our programmers really like. That's mainly because Rust is empowering: the quality of the code is much higher, and if you use it to compile a program, you can be confident that your software will run and go on running. That characteristic is simply built into in the language itself. As a result, programmers have more confidence in the software they produce. With a new application written in C/C++, by contrast, you usually need weeks of testing and tinkering before it works well and is stable. The upshot is that it's mainly in the final phase of development (the pathway from the first version to a production-ready product) that Rust's inherent robustness translates into improved productivity and reduced time to market. Maintenance and further development are less demanding as well."
Significant Rust pioneers include the Big Tech giants Google [1], Amazon (AWS) and Microsoft [1]. Last month, for example, Google announced that it was investing a million dollars into the Rust Foundation to improve interoperability between Rust and C++ [1]. (As indicated above, interoperability with C is already good.) A few months earlier, Amazon introduced a Rust interface [API and SDK] for its AWS services. Meanwhile, Microsoft is replacing certain elements of Windows with Rust code [1].
In addition, Rust now features in the source code for Linux [Rust for Linux, Nova] and FreeBSD [1, 2]. Jonkers therefore sees Rust as here to stay. "Use of the language has now passed the tipping point, meaning that Rust is no longer in danger of disappearing. Use of Rust is also increasingly common in the industry [1]."
RustNL promotes the use of Rust and regularly organises meetups. Last year, the foundation teamed up with NLnet Labs and Tweede Golf to organise the first RustNL conference (following an earlier meetup about the use of Rust in critical infrastructures). This year's conference is scheduled for 7 and 8 May in Delft.
Tweede Golf specialises in Rust programming and has developed products such as teach-rs, a university Rust course that is now used at various institutions, including the Slovak University of Technology (STU). TU Delft has developed its own material for its Embedded Systems Lab [1, 2]. Tweede Golf has also done a reimplementation of sudo (sudo-rs) [@GitHub] and implementations of the Network Time Protocol (NTP, ntpd-rs) [@GitHub] and Precision Time Protocol (PTP; Statime [@GitHub]). The sponsors of those developments include Prossimo [1, 2, 3], the NLnet Foundation [1], the Sovereign Tech Fund [1] and SIDN Fund [1].
Through Prossimo, the Sovereign Tech Fund is additionally funding the development of Rustls [@GitHub] (as the 'replacement' for OpenSSL) and Hickory DNS [@GitHub] (previously Trust-DNS, as the 'replacement' for BIND) [1].
The ntpd-rs and Statime software, which forms part of Project Pendulum [@GitHub], is of particular significance for us, because SIDN Labs has been running a public NTP/NTS service [1, 2] since 2019 [1, 2, 3]. The plan is to replace the existing software with ntpd-rs in due course; an experimental version is already running at ntpd-rs.sidnlabs.nl. In addition, an experimental PTP service based on Statime is being developed for certain target groups [1]. The 2 experiments are ultimately intended to result in a completely new time system, which can be used not only for our TimeNL service, but also for other similar services [1].
The backbone network that connects all our clock systems is already based on the PTP protocol, enabling the Linux-based front-end clocks to be synchronised extremely precisely with the core clocks. "A new open-source implementation is good for the diversity of the PTP software landscape," says Research Engineer Marco Davids. "For Linux, the only options at the moment are LinuxPTP and PTPd, and the latter is not maintained to a very high level. Although the need isn't as urgent, another NTP implementation to supplement NTP, NTPsec and chrony would be useful as well."
Rust addresses the memory safety problem by linking (moving) every reference to a single variable (owner) and lifetime (scope), and making all copies explicit (cloning; using reference counting).
Pointers (e.g. a call by reference) are replaced by references, which can be borrowed, but can no longer be used as a memory address.
Arrays now have a fixed length and are therefore no longer stored in the heap but on the stack. Indexing is still possible, but indexes lose their validity if the relevant array no longer exits.
Pointers are replaced by slices (sub-arrays). Both the array indexes and the slice bounds are subject to extra checks to ensure that no out-of-bounds elements are addressed (bounds checking).
The variable-length arrays familiar from C/C++ are (somewhat confusingly) renamed Vectors in Rust. They are implemented as dynamic arrays (growable), whose bounds are monitored by making any additions explicit (append).
In addition to the (untagged) unions that we know from C/C++, Rust also offers tagged unions, renamed enums. The type of data in the union is always known.For an enumeration of the kind used in C/C++ (a collection of related constants) you now have a simple union (with a single, implicit tag).
The use of structs is much as it was in C/C++. However, Rust additionally features tuples: structs with anonymous fields, which are useful mainly for the rapid decomposition of return values.
Address pointers are still available, but are now known as smart pointers. Each smart pointer is enclosed by a small wrapper and some metadata to prevent uncontrolled access to the memory.
Smart pointers form the basis for all data structures that do not have a defined length and are placed on the heap: Boxes (memory blocks, like in malloc), vectors, strings/slices (both based on vectors) and other types of collections (e.g. hash maps).
More complex data structures, such as (linked) lists and (binary) trees are built on recursive data structures, broadly as they are in C/C++.
Rust provides memory safety for threads and the associated synchronisation (Mutex) and communication mechanisms (message passing) in much the same way as for data structures.
In effect, what Rust does is give the programmer responsibility for memory safety by means of a combination of limitations and cognitive administration. The programmer must always be aware of who owns a reference to a data structure, who wants to become the co-owner (i.e. be given a reference of their own), and who merely needs to borrow a reference.
Meanwhile, a C programmer already needs to know whether a data structure should go on the heap (call by reference) or on the stack (call by value), whether a value may be amended (const), and when data on the heap needs to be cleared out (freed). Conceptually, therefore, only the ownership of data structures is new.
Anyone that wants to can still use unsafe Rust. The main difference is that the unsafe version still allows you to make references to raw pointers and call up unsafe functions. However, the relevant code blocks and functions have to be explicitly declared 'unsafe'.
Most of the relevant features are intended to enable links to ("legacy") C code. If you'd like to look into this topic more closely, we recommend the ominously titled book The Rustonomicon.