Swanky: What's New?

In January 2024, we highlighted the latest release of Swanky, our suite of Rust libraries for secure computation. Since then we’ve made many changes to the codebase, both in terms of improving overall code quality and documentation, alongside new capabilities. It’s been a while since we’ve provided an update on these changes, so think of this as our biennial blog post on how things are going. And hey, maybe we’ll have even more frequent updates in the future!

Stabilizing crates

Swanky has existed largely as a secure computation framework for Galois projects, and as new projects have required certain secure computation capabilities over time, we’ve added them to Swanky. However, different projects have different code standards. Sometimes, we need a one-off proof-of-concept to demonstrate some capability, whereas other times we need a more robust implementation that will be used across multiple projects. This has resulted in Swanky containing a large number of capabilities that are all useful, but of various quality and usability.

As we’ve further developed Swanky, and found more external interest in the repo, we’ve found the need for more stability of certain capabilities. However, we do think it is important to maintain the “testbed” nature of Swanky for future research in secure computation, and thus we deem the ability to include “less robust” code still important.

With these tensions in mind, in July 2025, we split the repo into two types of crates: core and edge. For crates to be included in core, they need to achieve certain code, testing, and documentation requirements. Thus, there is an expectation that core crates will largely remain stable over time, and be easier to use by developers. Crates in edge do not necessarily have these expectations: they may be underdocumented, less robust, and may change dramatically over time. Why include these crates at all then? Because we still find them useful, and external developers have found them useful as well.

Currently, only a few crates are in core: crates related to error handling, serialization, and network comms. However, we plan to move more crates into core as they improve. In particular, we hope to move fancy-garbling (and its associated crates) to core soon. Stay tuned!

New party crate

Writing secure computation protocols is often annoying, because there are multiple parties that largely do the same thing, but not always. So you can either implement multiple types that copy a bunch of code, or a single type with a bunch of (runtime) if statements to determine which party code to run. We previously introduced the swanky-party crate to address this issue, although due to its origin as a crate for zero-knowledge proofs it was tied to a Prover and Verifier party, where the Prover can have secret input, and the Verifier cannot. Our new version removes this restriction, alongside other niceties, such as the ability to define arbitrary party names (through the party_system! macro) or even define protocols over generic parties, so it can be used with any party system.

New network comms crate

We’ve introduced a new means for doing network communication, deprecating the old AbstractChannel approach in favor of a new swanky-channel crate. The old AbstractChannel approach had several deficiencies, in particular the need to manually “flush” the channel when the sending party was done sending data. This caused numerous headaches when using, because if the flush wasn’t included in the right spot execution would hang, making debugging a quite painful process. In the new API, such intricacies are all handled internally in the Channel type, which can be instantiated by wrapping any type that implements Read and Write. For example, the following wraps a TCP connection in a Channel:

fn do_crypto_with_a_tcp_connection(conn: std::net::TcpStream) -> Result<()> {
  Channel::with(conn, |channel| {
    channel.write_bytes(b"hello!")?;
    Ok(())
    })
}

Much of Swanky has moved to this new API, although the old (deprecated) AbstractChannel approach still exists in places – we hope to remove those uses in the near future.

New APIs for writing secure computation programs

Originally, the main API for writing circuits to be run by secure computation existed in the fancy-garbling crate, a crate which implements secure two-party computation based on garbled circuits. The API defined a core trait, Fancy, which contained an associated type defining the wire type of the circuit, alongside operations such as encoding input values into wires, and outputting wires as values. There was then a slew of extension traits that enabled additional functionality: traits such as FancyBinary exposed operations for AND and XOR, and traits such as BinaryBundleGadgets exposed higher level operations, such as binary addition or binary multiplication. One could then utilize these traits to build larger circuits.

While this approach allowed us to build quite complex circuits, it suffered from several problems. For one, the only way to introduce reusable building blocks was through extension traits on top of the core Fancy trait, which is not particularly scalable as the library of circuits grows. Secondly, there was no unified API for circuit components, making testing laborious (as every circuit had a potentially different API) and hampering usability. Lastly, the API was tied to fancy-garbling, even though a circuit itself should not be tied to a particular secure computation paradigm. For example, an AES circuit by itself is just a wiring of AND and XOR gates, and shouldn’t be tied to how those gates are computed by a particular protocol.

Our new API addresses all of these issues. We stick to having the core Fancy trait, alongside extension traits that define core operations (e.g., FancyBinary which exposes AND and XOR operations, and FancyOutput which exposes converting a wire into its underlying plaintext value). In addition, we introduce a Circuit trait to provide a generic circuit representation:

pub trait Circuit<F: Fancy> {
    /// The input type of the circuit.
    type Input;
    /// The output type of the circuit.
    ///
    /// The [`Flatten`] trait allows the output type to be converted into a
    /// `Vec<F::Item>`.
    type Output: Flatten<Item = F::Item>;‍ 
    
    /// Execute a circuit on a given [`Fancy`] backend using the provided inputs.
    fn execute(
    &self,
    backend: &mut F,
    inputs: Self::Input,
    channel: &mut Channel,
    ) -> Result<Self::Output>;
}

Suppose we’d like to implement a 64-bit binary addition circuit. This requires FancyBinary (we’re operating over bits after all), and takes two 64-bit arrays and outputs a single 64-bit array, alongside a carry bit:

struct BinaryAdder;

impl<F: FancyBinary> Circuit<F> for BinaryAdder {
    type Input = ([F::Item; 64], [F::Item; 64])
    type Output = ([F::Item; 64], F::Item)‍ 
    
    fn execute(
    &self,
    backend: &mut F,
    inputs: Self::Input,
    channel: &mut Channel,
    ) -> Result<Self::Output> {
       ...
    }
}

If we’d like to utilize this circuit in some other circuit, it’s a simple matter of calling BinaryAdder.execute with the appropriate inputs.

We’ve moved our entire circuit library to this new API, introducing new crates in the process. In particular, the fancy-traits crate contains the core Fancy traits for writing circuits, and fancy-circuits is our core circuit library (with everything from arithmetic over binary values to cryptographic operations such as AES or SHA256). Other new crates include fancy-analyzer (for computing statistics about circuits, such as the circuit depth and number of AND gates), and fancy-plaintext (for plaintext evaluation, which is great for testing circuit correctness).

Malicious garbled circuits

We’ve recently added swanky-authenticated-garbling, a crate for doing maliciously secure garbled circuits based on a protocol by Katz, Ranelluci, Rosulek, and Wang. As part of this effort we also added swanky-authenticated-bits for generating authenticated bits, shares, and triples — important building blocks for authenticated garbling. With our new circuit API discussed above, it is now easy to plug in all the same circuits that worked for our semi-honest implementation (fancy-garbling) into our maliciously secure implementation. We aim to still improve the overall performance of the protocol — while we can garble and evaluate around 10 million AND gates during online execution, this drops to only 100K AND gates when including the offline time — but we hope this initial version could prove useful for folks experimenting with garbled circuits.

Next steps

The above highlights just some of the changes we’ve made over the last few years (including, but not mentioned above: a new error type for swanky crates, major improvements to the schmivitz zero-knowledge proof protocol, and many others), and we’ve got a lot more incoming. We plan to continue moving crates over to the new APIs (in particular, the oblivious transfer (OT) and private set intersection (PSI) crates need some modernization love), deprecate old crates that we’re no longer planning to maintain (humidor and mac-and-cheese come to mind), and other such niceties.

If you’re interested in using Swanky, please reach out to us at swanky@galois.com. We have also been having internal discussions on hosting a workshop on Swanky sometime in 2027. If that is of interest, please reach out! We’d love to get a sense of the number of folks who might be interested, so reaching out is your best chance that such a workshop will happen.