Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ring API Compatibility (ring-native-ossl)

ring-native-ossl provides a structural drop-in for the ring cryptography crate, backed entirely by OpenSSL via native-ossl. Code that reaches ring through a selectable backend or feature flag can substitute this crate without changing call sites.

Adding the dependency

[dependencies]
ring-native-ossl = { path = "../ring-native-ossl" }

To use it as a transparent alias at the module level:

use ring_native_ossl as ring;

Module overview

Modulering equivalentProvided by
digestring::digestDigestAlg + DigestCtx
hmacring::hmacHmacCtx / MacCtx
hkdfring::hkdfHkdfBuilder
aeadring::aeadCipherAlg + AeadEncryptCtx/AeadDecryptCtx
agreementring::agreementPkey<Private> + DeriveCtx
signaturering::signaturePkey<Private> + Signer/Verifier
randring::randRand::fill
errorring::error

digest — Hashing

use ring_native_ossl::digest::{self, Context, SHA256, SHA384, SHA512};

// One-shot
let d = digest::digest(&SHA256, b"hello world");
println!("{}", hex::encode(d.as_ref()));

// Streaming
let mut ctx = Context::new(&SHA256);
ctx.update(b"hello ");
ctx.update(b"world");
let d = ctx.finish();

Algorithms

StaticDigest
SHA256SHA-256
SHA384SHA-384
SHA512SHA-512
SHA1_FOR_LEGACY_USE_ONLYSHA-1 (avoid in new code)

Context

impl Context {
    pub fn new(algorithm: &'static Algorithm) -> Self;
    pub fn update(&mut self, data: &[u8]);
    pub fn clone(&self) -> Self;           // fork mid-stream
    pub fn finish(self) -> Digest;
}

impl Digest {
    pub fn as_ref(&self) -> &[u8];
    pub fn algorithm(&self) -> &'static Algorithm;
}

hmac — Message Authentication

use ring_native_ossl::hmac::{self, Key, HMAC_SHA256};

let key = Key::new(HMAC_SHA256, b"my-secret-key");

// Sign
let tag = hmac::sign(&key, b"message");

// Verify
hmac::verify(&key, b"message", tag.as_ref()).unwrap();

Algorithms

StaticMAC
HMAC_SHA256HMAC-SHA-256
HMAC_SHA384HMAC-SHA-384
HMAC_SHA512HMAC-SHA-512

Key

impl Key {
    pub fn new(algorithm: Algorithm, key_bytes: &[u8]) -> Self;
    pub fn algorithm(&self) -> Algorithm;
}

pub fn sign(key: &Key, data: &[u8]) -> Tag;
pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified>;

Key material is stored in SecretBuf and zeroed on drop via OPENSSL_cleanse.


hkdf — Key Derivation

use ring_native_ossl::hkdf::{self, Salt, HKDF_SHA256};

// Extract + expand (full HKDF)
let salt = Salt::new(HKDF_SHA256, b"random-salt");
let prk  = salt.extract(b"input-key-material");

struct MyKeyType(usize);
impl hkdf::KeyType for MyKeyType {
    fn len(&self) -> usize { self.0 }
}

let mut okm_bytes = [0u8; 32];
prk.expand(&[b"info"], &MyKeyType(32))
   .unwrap()
   .fill(&mut okm_bytes)
   .unwrap();

Algorithms

StaticHash
HKDF_SHA256HKDF-SHA-256
HKDF_SHA384HKDF-SHA-384
HKDF_SHA512HKDF-SHA-512

Types

impl Salt {
    pub fn new(algorithm: Algorithm, value: &[u8]) -> Self;
    pub fn extract(self, secret: &[u8]) -> Prk;
    pub fn algorithm(&self) -> Algorithm;
}

impl Prk {
    /// Construct a PRK directly from bytes (skips extract step).
    pub fn new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self;

    /// Expand the PRK into output key material.
    pub fn expand<'a, L: KeyType>(
        &'a self,
        info: &'a [&[u8]],
        len:  L,
    ) -> Result<Okm<'a, L>, Unspecified>;
}

impl<'a, L: KeyType> Okm<'a, L> {
    pub fn len(&self) -> &L;
    pub fn fill(self, out: &mut [u8]) -> Result<(), Unspecified>;
}

The KeyType trait lets callers express the desired output length as a typed value (e.g. a newtype wrapping usize) so the length is checked at fill time.


aead — Authenticated Encryption

use ring_native_ossl::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM, NONCE_LEN};

let key_bytes = [0u8; 32];
let key = LessSafeKey::new(UnboundKey::new(&AES_256_GCM, &key_bytes).unwrap());

let nonce = Nonce::assume_unique_for_key([0u8; NONCE_LEN]);

// Encrypt (appends tag in-place)
let mut in_out = b"plaintext".to_vec();
key.seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out).unwrap();

// Decrypt (removes tag in-place, returns plaintext slice)
let nonce = Nonce::assume_unique_for_key([0u8; NONCE_LEN]);
let plaintext = key.open_in_place(nonce, Aad::empty(), &mut in_out).unwrap();

Algorithms

StaticCipherKeyTag
AES_128_GCMAES-128-GCM16 bytes16 bytes
AES_256_GCMAES-256-GCM32 bytes16 bytes
CHACHA20_POLY1305ChaCha20-Poly130532 bytes16 bytes

UnboundKey and LessSafeKey

impl UnboundKey {
    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8])
        -> Result<Self, Unspecified>;
    pub fn algorithm(&self) -> &'static Algorithm;
}

impl LessSafeKey {
    pub fn new(key: UnboundKey) -> Self;
    pub fn algorithm(&self) -> &'static Algorithm;

    /// Encrypt `in_out` in place; append the authentication tag.
    pub fn seal_in_place_append_tag<A: AsRef<[u8]>>(
        &self,
        nonce:  Nonce,
        aad:    Aad<A>,
        in_out: &mut Vec<u8>,
    ) -> Result<(), Unspecified>;

    /// Decrypt `in_out` in place; return a slice of the plaintext (within the buffer).
    pub fn open_in_place<'in_out, A: AsRef<[u8]>>(
        &self,
        nonce:  Nonce,
        aad:    Aad<A>,
        in_out: &'in_out mut [u8],
    ) -> Result<&'in_out mut [u8], Unspecified>;

    /// Decrypt only the bytes in `ciphertext_and_tag[src]`.
    pub fn open_within<'in_out, A: AsRef<[u8]>>(
        &self,
        nonce:  Nonce,
        aad:    Aad<A>,
        in_out: &'in_out mut [u8],
        src:    std::ops::RangeFrom<usize>,
    ) -> Result<&'in_out mut [u8], Unspecified>;
}

Key material (UnboundKey.key, LessSafeKey.key) is stored in SecretBuf and zeroed on drop.

Nonce construction

// From a fixed 12-byte array (caller guarantees uniqueness per key)
let nonce = Nonce::assume_unique_for_key([0u8; 12]);

// From a byte slice (checked length)
let nonce = Nonce::try_assume_unique_for_key(&bytes)?;

agreement — Ephemeral Key Exchange

use ring_native_ossl::agreement::{self, EphemeralPrivateKey, UnparsedPublicKey, X25519};
use ring_native_ossl::rand::SystemRandom;

let rng = SystemRandom::new();

let my_private  = EphemeralPrivateKey::generate(&X25519, &rng).unwrap();
let my_public   = my_private.compute_public_key().unwrap();

// The peer sends their raw public key bytes.
let peer_public = UnparsedPublicKey::new(&X25519, peer_pub_bytes);

let shared = agreement::agree_ephemeral(my_private, &peer_public, |key_material| {
    // `key_material` is the raw shared secret; derive from it here.
    key_material.to_vec()
}).unwrap();

Algorithms

StaticGroupPublic key
X25519X2551932 raw bytes
ECDH_P256P-25665 bytes (uncompressed 04‖x‖y)
ECDH_P384P-38497 bytes (uncompressed 04‖x‖y)

EC public keys are validated for correct length and uncompressed-point format (0x04 prefix) before the ECDH operation is attempted. Compressed points (0x02/0x03) are rejected with PeerMisbehaved.

EphemeralPrivateKey

impl EphemeralPrivateKey {
    pub fn generate(
        alg: &'static Algorithm,
        rng: &dyn SecureRandom,
    ) -> Result<Self, Unspecified>;

    pub fn compute_public_key(&self) -> Result<PublicKey, Unspecified>;
    pub fn algorithm(&self) -> &'static Algorithm;
}

EphemeralPrivateKey is consumed by agree_ephemeral — it cannot be reused across multiple exchanges, matching ring’s design intent.


signature — Signing and Verification

Signing with ECDSA

use ring_native_ossl::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_ASN1};

// Generate a fresh keypair
let kp = EcdsaKeyPair::generate(&ECDSA_P256_SHA256_ASN1).unwrap();
let sig = kp.sign(b"message to sign").unwrap();

// Or load from PKCS#8 DER
let kp = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1, pkcs8_bytes).unwrap();

Signing with Ed25519

use ring_native_ossl::signature::Ed25519KeyPair;

let kp  = Ed25519KeyPair::generate().unwrap();
let sig = kp.sign(b"message").unwrap();

Verification

use ring_native_ossl::signature::{self, UnparsedPublicKey, ECDSA_P256_SHA256_ASN1};

let pub_key = UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, pub_key_bytes);
pub_key.verify(b"message", sig.as_ref()).unwrap();

// or free function
signature::verify(&ECDSA_P256_SHA256_ASN1, pub_key_bytes, b"message", sig.as_ref()).unwrap();

Algorithms

StaticKey typeHashEncoding
ECDSA_P256_SHA256_ASN1EC P-256SHA-256DER (ASN.1)
ECDSA_P384_SHA384_ASN1EC P-384SHA-384DER (ASN.1)
ED25519Ed25519internalraw 64 bytes
RSA_PKCS1_SHA256RSASHA-256PKCS#1 v1.5
RSA_PKCS1_SHA384RSASHA-384PKCS#1 v1.5
RSA_PKCS1_SHA512RSASHA-512PKCS#1 v1.5
RSA_PSS_SHA256RSASHA-256PSS (max salt)
RSA_PSS_SHA384RSASHA-384PSS (max salt)
RSA_PSS_SHA512RSASHA-512PSS (max salt)

rand — Randomness

use ring_native_ossl::rand::{SecureRandom, SystemRandom};

let rng = SystemRandom::new();
let mut buf = [0u8; 32];
rng.fill(&mut buf).unwrap();

SystemRandom delegates to RAND_bytes via native_ossl::rand::Rand::fill.


error — Error Types

use ring_native_ossl::error::{KeyRejected, Unspecified};
Typering equivalentUsed for
Unspecifiedring::error::UnspecifiedAny cryptographic failure
KeyRejectedring::error::KeyRejectedInvalid key material

Both implement std::error::Error.


Limitations

The following ring APIs are not implemented:

  • ring::aead::SealingKey / OpeningKey — ring’s one-nonce-sequence API that enforces monotonic nonce advancement. Use LessSafeKey with your own nonce counter instead.
  • ring::signature::RsaKeyPair — RSA signing keypairs. RSA verification is fully supported. RSA signature generation can be done directly via native_ossl::pkey::Signer.
  • ring::signature::ECDSA_*_FIXED — fixed-size (P1363) ECDSA encoding. Only DER (ASN.1) encoding is provided.
  • ring::pbkdf2 — use native_ossl::kdf::Pbkdf2Builder directly.
  • ring::test — test utilities not applicable outside ring’s own test suite.