Product

Getting Rusty: Beyond Identity’s Journey to Memory Safety

Written By
Crispin Cowan
Published On
Jan 27, 2025

On May 1st 2024, CISA released their “Secure by Design Pledge” in which the signers commit to seven vital security steps to reduce the vulnerability of American software:

  1. Multi-factor Authentication (MFA): signers should use it
  2. Default Passwords: signers should not ship with default passwords
  3. Reducing Entire Classes of Vulnerability: signers should use tools that eliminate large classes of vulnerabilities, such as parameterized queries to prevent SQL injections, and memory-safe programming languages to prevent memory corruption
  4. Security Patches: signers commit to encouraging customers to deploy most security patches in a timely manner
  5. Vulnerability Disclosure Policy: signers should have one
  6. CVEs: signers commit to publicly disclosing their own vulnerabilities via CVE for critical and high vulnerabilities
  7. Evidence of Intrusions: signers commit to improving ability to detect intrusion in customer deployments

I immediately noticed that Beyond Identity was already doing quite a lot of this! Our flagship product is passwordless MFA. We don’t have passwords at all, much less default passwords. We do ship security patches, and we do have a vulnerability disclosure policy. But today’s topic is that we have been Rust-first (a memory-safe programming language) since before our first release.

How it started

We were not actually looking for memory safety, our goal was to find a platform that would support our code interfacing to host operating systems, across all of Windows, MacOS, Android, iOS, and Linux. Beyond Identity was founded in 2019, and by 2020 had begun implementation when we decided to retrench and look for a way to make our product portable. We considered a half dozen different platforms, and the first one we selected was actually GoLang!

Go is memory-safe and thoroughly cross-platform, so it was a viable candidate. However, Go posed several challenges for our usage. For example, both the host operating systems and the Go runtime catch and handle exceptions. Our team experienced issues with the Go runtime hijacking exceptions and crashing our application before we could catch and process it. Additionally, exception handling moves the stack pointer to where the handler is and fails to execute the object destructors for functions in between.  This potentially leaves sensitive data in memory where the stack used to be.

Rust turned out to be a better fit for us. While still providing robust coverage for all of our desired platforms, the Rust runtime is much leaner than Go’s runtime, essentially just a thread scheduler. Rust also does not support exception handling at all, making our architects happy. And so, in 2020, we committed to developing most of our code in Rust.

How it's going

Rust supports pointers while maintaining memory safety using an interesting abstraction of “lending” a variable from one function to another. The pointer has a lifetime that is bound to the owner’s scope. An issue with this is that the compiler refuses to compile code that might be memory-safe, but the compiler cannot prove it, causing frustration for developers.

Meanwhile, our Platform Authenticator (PA) endpoint agent is not particularly performance-intensive, because it only needs to do its thing once per transaction. As such, we found it easier for coding practice to use a “functional” style of programming, where buffers are copied around rather than passing pointers. It would make John McCarthy proud (Turing-award-winning inventor of Lisp).

Like many young programming languages, there is a dearth of API projections for Rust. However, all of our target platforms have full API projections for C, and so we expediently wrote Rust→C Foreign Function Interfaces (FFIs) and then let the respective operating systems handle the rest of the cross-platform issues. This is work, but not too arduous.

Similarly, there is a dearth of rich library functionality for Rust. Libraries are also amenable to the FFI approach, at the cost that now there is code inside the application that is not guaranteed to be memory-safe! We could undertake rewriting every library we need in Rust, but that dramatically increases the work factor of not just writing our product in Rust, but also re-implementing large volumes of code that is normally freely available, potentially creating new errors in the process

Even worse, various compliance regimes require that cryptography be performed only by certified cryptographic libraries, not home-brewed cryptography. This is not just required, but also security best practices. But it is problematic when no such libraries exist, forcing us to use FFIs to memory-unsafe libraries like OpenSSL. As a result, even though we have done the work of writing our applications in a memory-safe language, anyone attempting to build a crypto-compliant application in memory-safe languages is still potentially vulnerable to memory corruption vulnerabilities in the cryptographic libraries. Something akin to Heartbleed could still impact such products.

Lessons for us, you, and them

What we learned from this and share here with you, is that the work to implement rich systems applications in memory-safe languages is mostly about bridging the gap to the host OS, which likely has not provisioned API projections and libraries for the memory-safe language, and so you may have to do a lot of it yourself.

The message for mythical “them” is that it is vital that someone undertake to create a memory-safe equivalent of certified cryptographic libraries in a memory safe language. Until such memory-safe crypto libraries are built, it is not possible to create an application that is both memory-safe and crypto-compliance conforming, essential compromises of one form or another are required.

Get started with Device360 today

Getting Rusty: Beyond Identity’s Journey to Memory Safety

Download

On May 1st 2024, CISA released their “Secure by Design Pledge” in which the signers commit to seven vital security steps to reduce the vulnerability of American software:

  1. Multi-factor Authentication (MFA): signers should use it
  2. Default Passwords: signers should not ship with default passwords
  3. Reducing Entire Classes of Vulnerability: signers should use tools that eliminate large classes of vulnerabilities, such as parameterized queries to prevent SQL injections, and memory-safe programming languages to prevent memory corruption
  4. Security Patches: signers commit to encouraging customers to deploy most security patches in a timely manner
  5. Vulnerability Disclosure Policy: signers should have one
  6. CVEs: signers commit to publicly disclosing their own vulnerabilities via CVE for critical and high vulnerabilities
  7. Evidence of Intrusions: signers commit to improving ability to detect intrusion in customer deployments

I immediately noticed that Beyond Identity was already doing quite a lot of this! Our flagship product is passwordless MFA. We don’t have passwords at all, much less default passwords. We do ship security patches, and we do have a vulnerability disclosure policy. But today’s topic is that we have been Rust-first (a memory-safe programming language) since before our first release.

How it started

We were not actually looking for memory safety, our goal was to find a platform that would support our code interfacing to host operating systems, across all of Windows, MacOS, Android, iOS, and Linux. Beyond Identity was founded in 2019, and by 2020 had begun implementation when we decided to retrench and look for a way to make our product portable. We considered a half dozen different platforms, and the first one we selected was actually GoLang!

Go is memory-safe and thoroughly cross-platform, so it was a viable candidate. However, Go posed several challenges for our usage. For example, both the host operating systems and the Go runtime catch and handle exceptions. Our team experienced issues with the Go runtime hijacking exceptions and crashing our application before we could catch and process it. Additionally, exception handling moves the stack pointer to where the handler is and fails to execute the object destructors for functions in between.  This potentially leaves sensitive data in memory where the stack used to be.

Rust turned out to be a better fit for us. While still providing robust coverage for all of our desired platforms, the Rust runtime is much leaner than Go’s runtime, essentially just a thread scheduler. Rust also does not support exception handling at all, making our architects happy. And so, in 2020, we committed to developing most of our code in Rust.

How it's going

Rust supports pointers while maintaining memory safety using an interesting abstraction of “lending” a variable from one function to another. The pointer has a lifetime that is bound to the owner’s scope. An issue with this is that the compiler refuses to compile code that might be memory-safe, but the compiler cannot prove it, causing frustration for developers.

Meanwhile, our Platform Authenticator (PA) endpoint agent is not particularly performance-intensive, because it only needs to do its thing once per transaction. As such, we found it easier for coding practice to use a “functional” style of programming, where buffers are copied around rather than passing pointers. It would make John McCarthy proud (Turing-award-winning inventor of Lisp).

Like many young programming languages, there is a dearth of API projections for Rust. However, all of our target platforms have full API projections for C, and so we expediently wrote Rust→C Foreign Function Interfaces (FFIs) and then let the respective operating systems handle the rest of the cross-platform issues. This is work, but not too arduous.

Similarly, there is a dearth of rich library functionality for Rust. Libraries are also amenable to the FFI approach, at the cost that now there is code inside the application that is not guaranteed to be memory-safe! We could undertake rewriting every library we need in Rust, but that dramatically increases the work factor of not just writing our product in Rust, but also re-implementing large volumes of code that is normally freely available, potentially creating new errors in the process

Even worse, various compliance regimes require that cryptography be performed only by certified cryptographic libraries, not home-brewed cryptography. This is not just required, but also security best practices. But it is problematic when no such libraries exist, forcing us to use FFIs to memory-unsafe libraries like OpenSSL. As a result, even though we have done the work of writing our applications in a memory-safe language, anyone attempting to build a crypto-compliant application in memory-safe languages is still potentially vulnerable to memory corruption vulnerabilities in the cryptographic libraries. Something akin to Heartbleed could still impact such products.

Lessons for us, you, and them

What we learned from this and share here with you, is that the work to implement rich systems applications in memory-safe languages is mostly about bridging the gap to the host OS, which likely has not provisioned API projections and libraries for the memory-safe language, and so you may have to do a lot of it yourself.

The message for mythical “them” is that it is vital that someone undertake to create a memory-safe equivalent of certified cryptographic libraries in a memory safe language. Until such memory-safe crypto libraries are built, it is not possible to create an application that is both memory-safe and crypto-compliance conforming, essential compromises of one form or another are required.

Getting Rusty: Beyond Identity’s Journey to Memory Safety

Beyond Identity is Rust-first and have been since first-ship. This is unusual across the software industry. Learn more about our journey to strong memory safety.

On May 1st 2024, CISA released their “Secure by Design Pledge” in which the signers commit to seven vital security steps to reduce the vulnerability of American software:

  1. Multi-factor Authentication (MFA): signers should use it
  2. Default Passwords: signers should not ship with default passwords
  3. Reducing Entire Classes of Vulnerability: signers should use tools that eliminate large classes of vulnerabilities, such as parameterized queries to prevent SQL injections, and memory-safe programming languages to prevent memory corruption
  4. Security Patches: signers commit to encouraging customers to deploy most security patches in a timely manner
  5. Vulnerability Disclosure Policy: signers should have one
  6. CVEs: signers commit to publicly disclosing their own vulnerabilities via CVE for critical and high vulnerabilities
  7. Evidence of Intrusions: signers commit to improving ability to detect intrusion in customer deployments

I immediately noticed that Beyond Identity was already doing quite a lot of this! Our flagship product is passwordless MFA. We don’t have passwords at all, much less default passwords. We do ship security patches, and we do have a vulnerability disclosure policy. But today’s topic is that we have been Rust-first (a memory-safe programming language) since before our first release.

How it started

We were not actually looking for memory safety, our goal was to find a platform that would support our code interfacing to host operating systems, across all of Windows, MacOS, Android, iOS, and Linux. Beyond Identity was founded in 2019, and by 2020 had begun implementation when we decided to retrench and look for a way to make our product portable. We considered a half dozen different platforms, and the first one we selected was actually GoLang!

Go is memory-safe and thoroughly cross-platform, so it was a viable candidate. However, Go posed several challenges for our usage. For example, both the host operating systems and the Go runtime catch and handle exceptions. Our team experienced issues with the Go runtime hijacking exceptions and crashing our application before we could catch and process it. Additionally, exception handling moves the stack pointer to where the handler is and fails to execute the object destructors for functions in between.  This potentially leaves sensitive data in memory where the stack used to be.

Rust turned out to be a better fit for us. While still providing robust coverage for all of our desired platforms, the Rust runtime is much leaner than Go’s runtime, essentially just a thread scheduler. Rust also does not support exception handling at all, making our architects happy. And so, in 2020, we committed to developing most of our code in Rust.

How it's going

Rust supports pointers while maintaining memory safety using an interesting abstraction of “lending” a variable from one function to another. The pointer has a lifetime that is bound to the owner’s scope. An issue with this is that the compiler refuses to compile code that might be memory-safe, but the compiler cannot prove it, causing frustration for developers.

Meanwhile, our Platform Authenticator (PA) endpoint agent is not particularly performance-intensive, because it only needs to do its thing once per transaction. As such, we found it easier for coding practice to use a “functional” style of programming, where buffers are copied around rather than passing pointers. It would make John McCarthy proud (Turing-award-winning inventor of Lisp).

Like many young programming languages, there is a dearth of API projections for Rust. However, all of our target platforms have full API projections for C, and so we expediently wrote Rust→C Foreign Function Interfaces (FFIs) and then let the respective operating systems handle the rest of the cross-platform issues. This is work, but not too arduous.

Similarly, there is a dearth of rich library functionality for Rust. Libraries are also amenable to the FFI approach, at the cost that now there is code inside the application that is not guaranteed to be memory-safe! We could undertake rewriting every library we need in Rust, but that dramatically increases the work factor of not just writing our product in Rust, but also re-implementing large volumes of code that is normally freely available, potentially creating new errors in the process

Even worse, various compliance regimes require that cryptography be performed only by certified cryptographic libraries, not home-brewed cryptography. This is not just required, but also security best practices. But it is problematic when no such libraries exist, forcing us to use FFIs to memory-unsafe libraries like OpenSSL. As a result, even though we have done the work of writing our applications in a memory-safe language, anyone attempting to build a crypto-compliant application in memory-safe languages is still potentially vulnerable to memory corruption vulnerabilities in the cryptographic libraries. Something akin to Heartbleed could still impact such products.

Lessons for us, you, and them

What we learned from this and share here with you, is that the work to implement rich systems applications in memory-safe languages is mostly about bridging the gap to the host OS, which likely has not provisioned API projections and libraries for the memory-safe language, and so you may have to do a lot of it yourself.

The message for mythical “them” is that it is vital that someone undertake to create a memory-safe equivalent of certified cryptographic libraries in a memory safe language. Until such memory-safe crypto libraries are built, it is not possible to create an application that is both memory-safe and crypto-compliance conforming, essential compromises of one form or another are required.

Getting Rusty: Beyond Identity’s Journey to Memory Safety

Phishing resistance in security solutions has become a necessity. Learn the differences between the solutions and what you need to be phishing resistant.

On May 1st 2024, CISA released their “Secure by Design Pledge” in which the signers commit to seven vital security steps to reduce the vulnerability of American software:

  1. Multi-factor Authentication (MFA): signers should use it
  2. Default Passwords: signers should not ship with default passwords
  3. Reducing Entire Classes of Vulnerability: signers should use tools that eliminate large classes of vulnerabilities, such as parameterized queries to prevent SQL injections, and memory-safe programming languages to prevent memory corruption
  4. Security Patches: signers commit to encouraging customers to deploy most security patches in a timely manner
  5. Vulnerability Disclosure Policy: signers should have one
  6. CVEs: signers commit to publicly disclosing their own vulnerabilities via CVE for critical and high vulnerabilities
  7. Evidence of Intrusions: signers commit to improving ability to detect intrusion in customer deployments

I immediately noticed that Beyond Identity was already doing quite a lot of this! Our flagship product is passwordless MFA. We don’t have passwords at all, much less default passwords. We do ship security patches, and we do have a vulnerability disclosure policy. But today’s topic is that we have been Rust-first (a memory-safe programming language) since before our first release.

How it started

We were not actually looking for memory safety, our goal was to find a platform that would support our code interfacing to host operating systems, across all of Windows, MacOS, Android, iOS, and Linux. Beyond Identity was founded in 2019, and by 2020 had begun implementation when we decided to retrench and look for a way to make our product portable. We considered a half dozen different platforms, and the first one we selected was actually GoLang!

Go is memory-safe and thoroughly cross-platform, so it was a viable candidate. However, Go posed several challenges for our usage. For example, both the host operating systems and the Go runtime catch and handle exceptions. Our team experienced issues with the Go runtime hijacking exceptions and crashing our application before we could catch and process it. Additionally, exception handling moves the stack pointer to where the handler is and fails to execute the object destructors for functions in between.  This potentially leaves sensitive data in memory where the stack used to be.

Rust turned out to be a better fit for us. While still providing robust coverage for all of our desired platforms, the Rust runtime is much leaner than Go’s runtime, essentially just a thread scheduler. Rust also does not support exception handling at all, making our architects happy. And so, in 2020, we committed to developing most of our code in Rust.

How it's going

Rust supports pointers while maintaining memory safety using an interesting abstraction of “lending” a variable from one function to another. The pointer has a lifetime that is bound to the owner’s scope. An issue with this is that the compiler refuses to compile code that might be memory-safe, but the compiler cannot prove it, causing frustration for developers.

Meanwhile, our Platform Authenticator (PA) endpoint agent is not particularly performance-intensive, because it only needs to do its thing once per transaction. As such, we found it easier for coding practice to use a “functional” style of programming, where buffers are copied around rather than passing pointers. It would make John McCarthy proud (Turing-award-winning inventor of Lisp).

Like many young programming languages, there is a dearth of API projections for Rust. However, all of our target platforms have full API projections for C, and so we expediently wrote Rust→C Foreign Function Interfaces (FFIs) and then let the respective operating systems handle the rest of the cross-platform issues. This is work, but not too arduous.

Similarly, there is a dearth of rich library functionality for Rust. Libraries are also amenable to the FFI approach, at the cost that now there is code inside the application that is not guaranteed to be memory-safe! We could undertake rewriting every library we need in Rust, but that dramatically increases the work factor of not just writing our product in Rust, but also re-implementing large volumes of code that is normally freely available, potentially creating new errors in the process

Even worse, various compliance regimes require that cryptography be performed only by certified cryptographic libraries, not home-brewed cryptography. This is not just required, but also security best practices. But it is problematic when no such libraries exist, forcing us to use FFIs to memory-unsafe libraries like OpenSSL. As a result, even though we have done the work of writing our applications in a memory-safe language, anyone attempting to build a crypto-compliant application in memory-safe languages is still potentially vulnerable to memory corruption vulnerabilities in the cryptographic libraries. Something akin to Heartbleed could still impact such products.

Lessons for us, you, and them

What we learned from this and share here with you, is that the work to implement rich systems applications in memory-safe languages is mostly about bridging the gap to the host OS, which likely has not provisioned API projections and libraries for the memory-safe language, and so you may have to do a lot of it yourself.

The message for mythical “them” is that it is vital that someone undertake to create a memory-safe equivalent of certified cryptographic libraries in a memory safe language. Until such memory-safe crypto libraries are built, it is not possible to create an application that is both memory-safe and crypto-compliance conforming, essential compromises of one form or another are required.

Book

Getting Rusty: Beyond Identity’s Journey to Memory Safety

Phishing resistance in security solutions has become a necessity. Learn the differences between the solutions and what you need to be phishing resistant.

Download the book

By clicking “Accept All Cookies”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.