# Migrating from OpenSSL
Guide for Rust developers moving from OpenSSL bindings (`openssl` crate or manual
`openssl-sys` calls) to the synta and synta-certificate crates.
## Why migrate
| Safety | Unsafe FFI under the hood | Pure Rust; no unsafe in hot paths |
| Error handling | `openssl::error::ErrorStack` (global queue) | `synta::Error` (return value) |
| Encoding API | `i2d_*` / `d2i_*` C round-trips | `Encoder` / `Decoder` types |
| Lifetime complexity | `ForeignType` references, `&OpenSslRef` | Rust lifetimes on borrowed types |
| no_std | Not supported | Supported (alloc-only feature level) |
| Post-quantum | Not supported in stable builds | ML-DSA / ML-KEM built-in |
## Decoding comparison
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::asn1::Asn1Integer;
use openssl::bn::BigNum;
let asn1_int = Asn1Integer::from_der(data)?;
let bn = asn1_int.to_bn()?;
let value = bn.to_dec_str()?;
```
**Synta**
```rust,ignore
use synta::{Decoder, Encoding};
use synta::Integer;
let mut dec = Decoder::new(data, Encoding::Der);
let integer: Integer = dec.decode()?;
let value: i64 = integer.as_i64().unwrap_or_default();
```
## Encoding comparison
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::asn1::Asn1Integer;
use openssl::bn::BigNum;
let bn = BigNum::from_u32(42)?;
let asn1_int = Asn1Integer::from_bn(&bn)?;
let der = asn1_int.to_der()?;
```
**Synta**
```rust,ignore
use synta::{Encoder, Encoding, Integer};
let mut enc = Encoder::new(Encoding::Der);
enc.encode(&Integer::from(42u32))?;
let der = enc.finish()?;
```
## X.509 certificate parsing
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::x509::X509;
let cert = X509::from_der(der)?;
let serial = cert.serial_number().to_bn()?.to_hex_str()?;
let issuer = cert.issuer_name();
let not_before = cert.not_before();
let not_after = cert.not_after();
```
**Synta**
```rust,ignore
use synta::{Decoder, Encoding};
use synta_certificate::Certificate;
let mut dec = Decoder::new(der, Encoding::Der);
let cert: Certificate = dec.decode()?;
let serial = &cert.tbs_certificate.serial_number;
let issuer = &cert.tbs_certificate.issuer;
let not_before = &cert.tbs_certificate.validity.not_before;
let not_after = &cert.tbs_certificate.validity.not_after;
```
## Certificate building
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::x509::{X509, X509Builder};
use openssl::x509::extension::BasicConstraints;
use openssl::pkey::PKey;
use openssl::hash::MessageDigest;
let mut builder = X509Builder::new()?;
builder.set_version(2)?;
builder.set_serial_number(&serial)?;
builder.set_issuer_name(&issuer_name)?;
builder.set_subject_name(&subject_name)?;
builder.set_not_before(¬_before)?;
builder.set_not_after(¬_after)?;
builder.set_pubkey(&pubkey)?;
let bc = BasicConstraints::new().critical().ca().build()?;
builder.append_extension(bc)?;
builder.sign(&private_key, MessageDigest::sha256())?;
let cert = builder.build();
let der = cert.to_der()?;
```
**Synta**
```rust,ignore
use synta::{Integer, UtcTime};
use synta_certificate::{
CertificateBuilder, OpensslCertificateSigner,
Time, encode_basic_constraints, oids,
};
use openssl::pkey::PKey;
let signer = OpensslCertificateSigner::new(&pkey, "sha256");
// Encode the BasicConstraints extension value (CA=true, pathLen=0)
let bc_der = encode_basic_constraints(true, Some(0)).unwrap();
let der = CertificateBuilder::new()
.serial_number(Integer::from(1i64))
.issuer_name(issuer_der)
.subject_name(subject_der)
.not_valid_before(Time::UtcTime(UtcTime::new(2024, 1, 1, 0, 0, 0).unwrap()))
.not_valid_after(Time::UtcTime(UtcTime::new(2025, 1, 1, 0, 0, 0).unwrap()))
.public_key_der(spki_der)
.add_extension_oid(oids::BASIC_CONSTRAINTS, true, &bc_der)
.sign(&signer)?;
```
## Certificate chain verification
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::ssl::{SslConnector, SslMethod};
use openssl::x509::store::X509StoreBuilder;
let mut store_builder = X509StoreBuilder::new()?;
store_builder.add_cert(root_cert)?;
let store = store_builder.build();
// OpenSSL verifies as part of TLS handshake or via X509_verify_cert
```
**Synta**
```rust,ignore
use synta_x509_verification::{
ops::VerificationCertificate,
policy::PolicyDefinition,
trust_store::Store,
types::DNSName,
verify, RevocationChecks,
};
use synta_certificate::OpensslSignatureVerifier;
let root = VerificationCertificate::new(parse(&root_der), &root_der);
let store = Store::new([root]);
let hostname = DNSName::new("example.com").unwrap();
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs() as i64;
let policy = PolicyDefinition::new_server(
OpensslSignatureVerifier,
vec![synta_x509_verification::policy::Subject::Dns(hostname)],
now,
);
let chain = verify(&leaf, &[intermediate], &policy, &store, RevocationChecks::default())?;
```
## Type mapping
| `openssl::asn1::Asn1Integer` | `synta::Integer` |
| `openssl::asn1::Asn1String` | `synta::Utf8String` / `synta::IA5String` / etc. |
| `openssl::asn1::Asn1Object` | `synta::ObjectIdentifier` |
| `openssl::asn1::Asn1OctetString` | `synta::OctetString` |
| `openssl::asn1::Asn1BitString` | `synta::BitString` |
| `openssl::x509::X509` | `synta_certificate::Certificate<'a>` |
| `openssl::x509::X509Name` | `synta_certificate::Name<'a>` |
| `openssl::x509::X509NameEntry` | `synta_certificate::AttributeTypeAndValue<'a>` |
| `openssl::x509::X509Crl` | `synta_certificate::crl::CertificateList<'a>` |
## Error handling
**OpenSSL (`openssl` crate)**
```rust,ignore
use openssl::error::ErrorStack;
match some_openssl_call() {
Ok(val) => { /* use val */ }
Err(e) => {
// ErrorStack: may contain multiple queued errors
eprintln!("OpenSSL error: {}", e);
}
}
```
**Synta**
```rust,ignore
use synta::Error;
match decoder.decode::<MyType>() {
Ok(val) => { /* use val */ }
Err(e) => {
// Single Error value with position info
eprintln!("Decode error: {}", e);
}
}
```
## Benefits of migrating
1. **No global error state** — errors are returned values, not extracted from a
thread-local queue.
2. **Lifetime-safe zero-copy** — borrowed variants (`OctetStringRef<'a>`,
`BitStringRef<'a>`) eliminate copies without unsafe code.
3. **Post-quantum ready** — ML-DSA and ML-KEM are first-class algorithm types with
parameter validation.
4. **no_std support** — drop `std` and keep `alloc` for embedded or WebAssembly
targets.
5. **Schema-driven codegen** — write ASN.1 schemas and generate complete, typed
structs rather than hand-coding decode/encode logic.