Skip to content
Blog ยป Ring by Example – Sample Code Cheatsheet

Ring by Example – Sample Code Cheatsheet

    I have been writing about the Ring cryptography library for a while now and so in this post I am sharing a resource which I think will be a useful reference for developers implementing applications or protocols using the library.

    This is basically a cheatsheet of example code showing how to use each of the cryptography primitives supported by Ring. I’ve tried to keep the examples as simple as possible in order to make the code samples easy to read through and as re-usable as possible. For a more detailed explanation on how to use each of the modules you can see my previous posts on each topic which are linked in the relevant sections below.

    Summary of Modules

    The Ring cryptography library contains the following modules for working with various cryptography primitives:

    The modules support primitives such as hash functions, random numbers, message authentication codes, key derivation, key agreement, digital signatures and authenticated encryption. The code samples below show how to use the APIs defined in each of these modules. Note that this post was written using version 0.16.20 of the library.

    Disclaimer: Make sure you understand what the code is doing before copying into any real world application.

    Cryptographic Hashes Using the Digest Module

    See the full post on using the ring::digest module here.

    use ring::digest;
    use ring::digest::Digest;
    use ring::digest::Context;
    use ring::digest::SHA256;
    
    fn main() {
        // Calculate a hash using the digest function
        let _digest: Digest = digest::digest(&SHA256, b"hello, world");
    
        // Calculate a hash using the Context struct
        let mut ctx = Context::new(&SHA256);
        ctx.update(b"hello, ");
        ctx.update(b"world");
    
        let _digest = ctx.finish();
    }

    Generating Random Numbers Using the Rand Module

    See the full post on using the ring::rand module here.

    use ring::error::Unspecified;
    use ring::rand;
    use ring::rand::SecureRandom;
    use ring::rand::SystemRandom;
    use ring::rand::Random;
    
    fn main() -> Result<(), Unspecified> {
        const NUM_SIZE_BYTES: usize = 32;
        let rand = SystemRandom::new();
    
        // Generate random number using the SecureRandom::fill method
        let mut rand_bytes = [0u8; NUM_SIZE_BYTES];
        rand.fill(&mut rand_bytes)?;
    
        // Generate random number using the rand::generate function
        let result: Random<[u8; NUM_SIZE_BYTES]> = rand::generate(&rand)?;
        let _rand_bytes = result.expose();
    
        Ok(())
    }

    Signing and Verification Using the HMAC Module

    See the full post on using the ring::hmac module here.

    use ring::error::Unspecified;
    use ring::rand::SystemRandom;
    use ring::hmac;
    use ring::hmac::Key;
    use ring::hmac::Context;
    use ring::hmac::HMAC_SHA256;
    
    fn main() -> Result<(), Unspecified> {
        let rand = SystemRandom::new();
        let key = Key::generate(HMAC_SHA256, &rand)?;
    
        // Sign a message and then verify using the hmac::sign function
        let tag = hmac::sign(&key, b"hello, world");
        hmac::verify(&key, b"hello, world", tag.as_ref())?;
    
        // Sign a message and then verify using the Context struct
        let mut ctx = Context::with_key(&key);
        ctx.update(b"hello, ");
        ctx.update(b"world");
        let tag = ctx.sign();
    
        hmac::verify(&key, b"hello, world", tag.as_ref())
    }

    Key Derivation Using the HKDF Module

    See the full post on using the ring::hkdf module here.

    use ring::error::Unspecified;
    use ring::digest::SHA256_OUTPUT_LEN;
    use ring::hkdf::Salt;
    use ring::hkdf::Prk;
    use ring::hkdf::Okm;
    use ring::hkdf::Algorithm;
    use ring::hkdf::HKDF_SHA256;
    
    fn main() -> Result<(), Unspecified> {
        // Derive a single output key using Salt::extract and Prk::expand
    
        let input_key_material = b"secret key";
    
        let salt = Salt::new(HKDF_SHA256, b"salt bytes");
        let pseudo_rand_key: Prk = salt.extract(input_key_material);
        let context_data = ["context field 1".as_bytes(), "context field 2".as_bytes()];
        let output_key_material: Okm<Algorithm> = pseudo_rand_key.expand(&context_data, HKDF_SHA256)?;
    
        let mut result = [0u8; SHA256_OUTPUT_LEN];
        output_key_material.fill(&mut result)
    }

    Password Hashing Using the PBKDF2 Module

    See the full post on using the ring::pbkdf2 module here.

    use ring::error::Unspecified;
    use std::num::NonZeroU32;
    use ring::digest::SHA256_OUTPUT_LEN;
    use ring::pbkdf2;
    use ring::pbkdf2::PBKDF2_HMAC_SHA256;
    
    fn main() -> Result<(), Unspecified> {
        // Derive a password hash and verify using pbkdf2::derive and pbkdf2::verify
    
        let iterations = NonZeroU32::new(600_000).unwrap();
        let salt = b"random salt";
        let secret = b"strong password";
    
        let mut password_hash = [0u8; SHA256_OUTPUT_LEN];
        pbkdf2::derive(PBKDF2_HMAC_SHA256, iterations, salt, secret, &mut password_hash);
        pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, secret, &password_hash)  
    }

    Key Exchange Using the Agreement Module

    See the full post on using the ring::agreement module here.

    use ring::error::Unspecified;
    use ring::rand::SystemRandom;
    use ring::agreement;
    use ring::agreement::Algorithm;
    use ring::agreement::X25519;
    use ring::agreement::EphemeralPrivateKey;
    use ring::agreement::PublicKey;
    use ring::agreement::UnparsedPublicKey;
    
    fn main() -> Result<(), Unspecified> {
        // Derived a shared secret using ECDH
    
        let rng = SystemRandom::new();
        let alg: &Algorithm = &X25519;
    
        let my_private_key: EphemeralPrivateKey = EphemeralPrivateKey::generate(alg, &rng)?;
        let _my_public_key: PublicKey = my_private_key.compute_public_key()?;
    
        // Send our public key to the peer here
    
        let peer_public_key: PublicKey = { // Simulate receiving a public key from the peer
            let peer_private_key = EphemeralPrivateKey::generate(alg, &rng)?;
            peer_private_key.compute_public_key()?
        };
    
        let peer_public_key = UnparsedPublicKey::new(alg, peer_public_key);
        agreement::agree_ephemeral(my_private_key,
                        &peer_public_key,
                        |_shared_secret: &[u8]| {
                            // use the shared secret
                        })
    }

    Signing and Verification Using the Signature Module

    See the full post on using the ring::signature module here.

    use ring::error::Unspecified;
    use ring::rand::SystemRandom;
    use ring::signature::ED25519;
    use ring::signature::KeyPair;
    use ring::signature::Ed25519KeyPair;
    use ring::signature::UnparsedPublicKey;
    
    fn main() -> Result<(), Unspecified> {
        // Sign and verify a message using EdDSA
    
        let rand = SystemRandom::new();
        let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rand)?;
        let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).map_err(|_| Unspecified)?;
    
        const MESSAGE: &[u8] = b"hello, world";
        let sig = key_pair.sign(MESSAGE);
    
        let peer_public_key_bytes = key_pair.public_key().as_ref();
        let peer_public_key = UnparsedPublicKey::new(&ED25519, peer_public_key_bytes);
        peer_public_key.verify(MESSAGE, sig.as_ref())
    }

    Authenticated Encryption Using the AEAD Module

    See the full post on using the ring::aead module here.

    use ring::error::Unspecified;
    use ring::rand::SecureRandom;
    use ring::rand::SystemRandom;
    use ring::aead::AES_256_GCM;
    use ring::aead::UnboundKey;
    use ring::aead::BoundKey;
    use ring::aead::SealingKey;
    use ring::aead::OpeningKey;
    use ring::aead::Aad;
    use ring::aead::NonceSequence;
    use ring::aead::NONCE_LEN;
    use ring::aead::Nonce;
    
    // Always returns the same nonce value for simplicity, don't use for more than 1 sealing operation!
    struct SingleNonceSequence([u8; NONCE_LEN]);
    
    impl NonceSequence for SingleNonceSequence {
        fn advance(&mut self) -> Result<Nonce, Unspecified> {
            Nonce::try_assume_unique_for_key(&self.0)
        }
    }
    
    fn main() -> Result<(), Unspecified> {
        // Encrypt and decrypt some data using AES-GCM-256
    
        let rand = SystemRandom::new();
    
        let mut key_bytes = vec![0; AES_256_GCM.key_len()];
        rand.fill(&mut key_bytes)?;
        let mut nonce_value = [0; NONCE_LEN];
        rand.fill(&mut nonce_value)?;
    
        let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
        let nonce_sequence = SingleNonceSequence(nonce_value);
        let mut sealing_key = SealingKey::new(unbound_key, nonce_sequence);
    
        let associated_data = Aad::from(b"additional public data");
        let data = b"hello world";
        let mut in_out = data.clone();
        let tag = sealing_key.seal_in_place_separate_tag(associated_data, &mut in_out)?;
    
        let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
        let nonce_sequence = SingleNonceSequence(nonce_value);
        let mut opening_key = OpeningKey::new(unbound_key, nonce_sequence);
    
        let associated_data = Aad::from(b"additional public data");
        let mut cypher_text_with_tag = [&in_out, tag.as_ref()].concat();
        let decrypted_data = opening_key.open_in_place(associated_data, &mut cypher_text_with_tag)?;
    
        assert_eq!(data, decrypted_data);
    
        Ok(())
    }

    Conclusion

    In this example code cheatsheet we covered how to use each of the Ring modules to implement cryptography primitives such as hash functions, random numbers, message authentication codes, key derivation, key agreement, digital signatures and authenticated encryption. If you want to read more about any of the topics check out the linked resources above as well.

    Thanks for reading. I hope you find this resource useful. If you have any questions about using the Ring cryptography library, feel free to reach out.

    Leave a Reply

    Your email address will not be published. Required fields are marked *