# Migrating from OpenSSL to Synta
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Why Migrate](#why-migrate)
- [Advantages of Synta](#advantages-of-synta)
- [Differences from OpenSSL](#differences-from-openssl)
- [API Comparison](#api-comparison)
- [Decoding](#decoding)
- [OpenSSL](#openssl)
- [Synta](#synta)
- [Encoding](#encoding)
- [OpenSSL](#openssl-1)
- [Synta](#synta-1)
- [Type Mapping](#type-mapping)
- [Basic Types](#basic-types)
- [Constructed Types](#constructed-types)
- [Error Handling](#error-handling)
- [Common Patterns](#common-patterns)
- [Pattern 1: Decode SEQUENCE](#pattern-1-decode-sequence)
- [OpenSSL](#openssl-2)
- [Synta](#synta-2)
- [Pattern 2: Encode SEQUENCE](#pattern-2-encode-sequence)
- [OpenSSL](#openssl-3)
- [Synta](#synta-3)
- [Pattern 3: OID Handling](#pattern-3-oid-handling)
- [OpenSSL](#openssl-4)
- [Synta](#synta-4)
- [Pattern 4: BIT STRING](#pattern-4-bit-string)
- [OpenSSL](#openssl-5)
- [Synta](#synta-5)
- [Migration Examples](#migration-examples)
- [Example 1: Parse X.509 Serial Number](#example-1-parse-x509-serial-number)
- [OpenSSL](#openssl-6)
- [Synta](#synta-6)
- [Example 2: Create Basic SEQUENCE](#example-2-create-basic-sequence)
- [OpenSSL](#openssl-7)
- [Synta](#synta-7)
- [Example 3: Context-Specific Tags](#example-3-context-specific-tags)
- [OpenSSL](#openssl-8)
- [Synta](#synta-8)
- [Example 4: Complete X.509 Certificate Parsing](#example-4-complete-x509-certificate-parsing)
- [OpenSSL Version](#openssl-version)
- [Synta Version](#synta-version)
- [Building and Running the Example](#building-and-running-the-example)
- [Benefits](#benefits)
- [1. Memory Safety](#1-memory-safety)
- [2. Simpler Error Handling](#2-simpler-error-handling)
- [3. No Global State](#3-no-global-state)
- [4. Clearer API](#4-clearer-api)
- [5. Better Performance](#5-better-performance)
- [See Also](#see-also)
Guide for developers migrating from OpenSSL's ASN.1 library to Synta.
## Table of Contents
- [Why Migrate](#why-migrate)
- [API Comparison](#api-comparison)
- [Type Mapping](#type-mapping)
- [Common Patterns](#common-patterns)
- [Migration Examples](#migration-examples)
- [Benefits](#benefits)
## Why Migrate
### Advantages of Synta
1. **Memory Safety**: Rust-based implementation with guaranteed memory safety
2. **Simpler API**: No manual memory management complexity
3. **Better Error Handling**: Clear error codes instead of global error state
4. **Modern Design**: Designed from scratch for safety and usability
5. **Zero Dependencies**: Minimal footprint
6. **Cross-Platform**: Consistent behavior across platforms
### Differences from OpenSSL
| Language | C | Rust (C FFI) |
| Memory Safety | Manual | Automatic (via Rust) |
| Error Handling | Global error queue | Return codes + thread-local messages |
| API Style | BIO-based, stateful | Decoder/Encoder pattern |
| Complexity | High (many options) | Lower (focused API) |
| Dependencies | Large | Minimal |
## API Comparison
### Decoding
#### OpenSSL
```c
#include <openssl/asn1.h>
// Decode INTEGER
const unsigned char *p = data;
ASN1_INTEGER *aint = d2i_ASN1_INTEGER(NULL, &p, len);
if (!aint) {
// Check ERR_get_error()
}
long value = ASN1_INTEGER_get(aint);
ASN1_INTEGER_free(aint);
```
#### Synta
```c
#include <synta.h>
// Decode INTEGER
SyntaDecoder *decoder = synta_decoder_new(data, len, SyntaEncoding_Der);
SyntaInteger *integer = NULL;
if (synta_decode_integer(decoder, &integer) == SyntaErrorCode_Success) {
int64_t value;
synta_integer_to_i64(integer, &value);
synta_integer_free(integer);
}
synta_decoder_free(decoder);
```
### Encoding
#### OpenSSL
```c
// Encode INTEGER
ASN1_INTEGER *aint = ASN1_INTEGER_new();
ASN1_INTEGER_set(aint, 42);
unsigned char *buf = NULL;
int len = i2d_ASN1_INTEGER(aint, &buf);
if (len < 0) {
// Error
}
// Use buf
OPENSSL_free(buf);
ASN1_INTEGER_free(aint);
```
#### Synta
```c
// Encode INTEGER
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
synta_encode_integer_i64(encoder, 42);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output.data and output.len
synta_byte_array_free(&output);
```
## Type Mapping
### Basic Types
| `ASN1_INTEGER` | `SyntaInteger` | Arbitrary precision |
| `ASN1_BOOLEAN` | `bool` | Plain C bool |
| `ASN1_NULL` | N/A | No object needed |
| `ASN1_OCTET_STRING` | `SyntaOctetString` | Byte array |
| `ASN1_BIT_STRING` | `SyntaByteArray` | Returns bytes + unused bits |
| `ASN1_OBJECT` | `SyntaObjectIdentifier` | OID |
| `ASN1_UTF8STRING` | `SyntaByteArray` | UTF-8 bytes |
| `ASN1_PRINTABLESTRING` | `SyntaByteArray` | ASCII bytes |
| `ASN1_IA5STRING` | `SyntaByteArray` | ASCII bytes |
### Constructed Types
| `ASN1_SEQUENCE_ANY` | `SyntaDecoder` (nested) | Enter sequence, decode elements |
| `ASN1_SET_ANY` | `SyntaDecoder` (nested) | Enter set, decode elements |
| `ASN1_CTX` | `SyntaTag` | Context-specific tags |
### Error Handling
| `ERR_get_error()` | `SyntaErrorCode` return value |
| `ERR_error_string()` | `synta_error_message()` |
| `ERR_clear_error()` | `synta_clear_last_error()` |
| Error queue | Thread-local last error |
## Common Patterns
### Pattern 1: Decode SEQUENCE
#### OpenSSL
```c
const unsigned char *p = data;
ASN1_SEQUENCE_ANY *seq = d2i_ASN1_SEQUENCE_ANY(NULL, &p, len);
if (!seq) {
return -1;
}
for (int i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
ASN1_TYPE *elem = sk_ASN1_TYPE_value(seq, i);
if (elem->type == V_ASN1_INTEGER) {
long val = ASN1_INTEGER_get(elem->value.integer);
}
}
ASN1_SEQUENCE_ANY_free(seq);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(data, len, SyntaEncoding_Der);
SyntaDecoder *seq_decoder = NULL;
if (synta_decoder_enter_sequence(decoder, &seq_decoder) == SyntaErrorCode_Success) {
while (!synta_decoder_at_end(seq_decoder)) {
// Peek at tag to determine type
SyntaTag tag;
synta_decoder_peek_tag(seq_decoder, &tag);
if (tag.class_ == SyntaTagClass_Universal && tag.number == 2) {
SyntaInteger *integer = NULL;
synta_decode_integer(seq_decoder, &integer);
// Use integer
synta_integer_free(integer);
}
}
synta_decoder_free(seq_decoder);
}
synta_decoder_free(decoder);
```
### Pattern 2: Encode SEQUENCE
#### OpenSSL
```c
ASN1_SEQUENCE_ANY *seq = ASN1_SEQUENCE_ANY_new();
ASN1_INTEGER *aint = ASN1_INTEGER_new();
ASN1_INTEGER_set(aint, 42);
ASN1_TYPE *type = ASN1_TYPE_new();
ASN1_TYPE_set(type, V_ASN1_INTEGER, aint);
sk_ASN1_TYPE_push(seq, type);
unsigned char *buf = NULL;
int len = i2d_ASN1_SEQUENCE_ANY(seq, &buf);
ASN1_SEQUENCE_ANY_free(seq);
OPENSSL_free(buf);
```
#### Synta
```c
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaEncoder *seq_encoder = NULL;
synta_encoder_start_sequence(encoder, &seq_encoder);
synta_encode_integer_i64(seq_encoder, 42);
synta_encoder_end_constructed(seq_encoder);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output
synta_byte_array_free(&output);
```
### Pattern 3: OID Handling
#### OpenSSL
```c
// Create OID
ASN1_OBJECT *obj = OBJ_txt2obj("1.2.840.113549.1.1.1", 1);
if (!obj) {
return -1;
}
// Convert to string
char buf[256];
OBJ_obj2txt(buf, sizeof(buf), obj, 1);
// Compare OIDs
int equal = OBJ_cmp(obj1, obj2) == 0;
ASN1_OBJECT_free(obj);
```
#### Synta
```c
// Create OID
SyntaObjectIdentifier *oid = synta_oid_from_string("1.2.840.113549.1.1.1");
if (!oid) {
return -1;
}
// Convert to string
char buf[256];
synta_oid_to_string(oid, buf, sizeof(buf));
// Compare OIDs
bool equal = synta_oid_equals(oid1, oid2);
synta_oid_free(oid);
```
### Pattern 4: BIT STRING
#### OpenSSL
```c
ASN1_BIT_STRING *bs = d2i_ASN1_BIT_STRING(NULL, &p, len);
if (!bs) {
return -1;
}
unsigned char *data = ASN1_STRING_data(bs);
int length = ASN1_STRING_length(bs);
int unused_bits = bs->flags & 0x07;
ASN1_BIT_STRING_free(bs);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(data, len, SyntaEncoding_Der);
SyntaByteArray bits = {0};
uint8_t unused_bits;
if (synta_decode_bit_string(decoder, &bits, &unused_bits) == SyntaErrorCode_Success) {
// Use bits.data, bits.len, unused_bits
synta_byte_array_free(&bits);
}
synta_decoder_free(decoder);
```
## Migration Examples
### Example 1: Parse X.509 Serial Number
#### OpenSSL
```c
X509 *cert = d2i_X509(NULL, &p, len);
if (!cert) {
return -1;
}
ASN1_INTEGER *serial = X509_get_serialNumber(cert);
BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
char *hex = BN_bn2hex(bn);
printf("Serial: %s\n", hex);
OPENSSL_free(hex);
BN_free(bn);
X509_free(cert);
```
#### Synta
```c
// Parse TBSCertificate manually
SyntaDecoder *decoder = synta_decoder_new(cert_data, cert_len, SyntaEncoding_Der);
SyntaDecoder *cert_seq = NULL;
synta_decoder_enter_sequence(decoder, &cert_seq);
SyntaDecoder *tbs = NULL;
synta_decoder_enter_sequence(cert_seq, &tbs);
// Skip version [0] if present
SyntaTag tag;
if (synta_decoder_peek_tag(tbs, &tag) == SyntaErrorCode_Success &&
tag.class_ == SyntaTagClass_ContextSpecific && tag.number == 0) {
SyntaDecoder *version_decoder = NULL;
synta_decoder_enter_constructed(tbs, tag, &version_decoder);
synta_decoder_free(version_decoder);
}
// Decode serial number
SyntaInteger *serial = NULL;
synta_decode_integer(tbs, &serial);
SyntaByteArray serial_bytes = {0};
synta_integer_to_bytes(serial, &serial_bytes);
// Print as hex
for (size_t i = 0; i < serial_bytes.len; i++) {
printf("%02X", serial_bytes.data[i]);
}
synta_byte_array_free(&serial_bytes);
synta_integer_free(serial);
synta_decoder_free(tbs);
synta_decoder_free(cert_seq);
synta_decoder_free(decoder);
```
### Example 2: Create Basic SEQUENCE
#### OpenSSL
```c
// SEQUENCE { INTEGER 1, UTF8String "test" }
ASN1_SEQUENCE_ANY *seq = ASN1_SEQUENCE_ANY_new();
ASN1_INTEGER *num = ASN1_INTEGER_new();
ASN1_INTEGER_set(num, 1);
ASN1_TYPE *t1 = ASN1_TYPE_new();
ASN1_TYPE_set(t1, V_ASN1_INTEGER, num);
sk_ASN1_TYPE_push(seq, t1);
ASN1_UTF8STRING *str = ASN1_UTF8STRING_new();
ASN1_STRING_set(str, "test", 4);
ASN1_TYPE *t2 = ASN1_TYPE_new();
ASN1_TYPE_set(t2, V_ASN1_UTF8STRING, str);
sk_ASN1_TYPE_push(seq, t2);
unsigned char *buf = NULL;
int len = i2d_ASN1_SEQUENCE_ANY(seq, &buf);
ASN1_SEQUENCE_ANY_free(seq);
OPENSSL_free(buf);
```
#### Synta
```c
// SEQUENCE { INTEGER 1, UTF8String "test" }
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaEncoder *seq_encoder = NULL;
synta_encoder_start_sequence(encoder, &seq_encoder);
synta_encode_integer_i64(seq_encoder, 1);
synta_encode_utf8_string(seq_encoder, "test");
synta_encoder_end_constructed(seq_encoder);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output
synta_byte_array_free(&output);
```
### Example 3: Context-Specific Tags
#### OpenSSL
```c
// [0] EXPLICIT INTEGER
ASN1_INTEGER *num = ASN1_INTEGER_new();
ASN1_INTEGER_set(num, 42);
unsigned char *buf = NULL;
int len = ASN1_item_ex_i2d((ASN1_VALUE **)&num, &buf,
ASN1_ITEM_rptr(ASN1_INTEGER),
0, // Tag 0
V_ASN1_CONTEXT_SPECIFIC);
OPENSSL_free(buf);
ASN1_INTEGER_free(num);
```
#### Synta
```c
// [0] EXPLICIT INTEGER
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaTag tag = {
.class_ = SyntaTagClass_ContextSpecific,
.constructed = true,
.number = 0
};
SyntaEncoder *ctx_encoder = NULL;
synta_encoder_start_constructed(encoder, tag, &ctx_encoder);
synta_encode_integer_i64(ctx_encoder, 42);
synta_encoder_end_constructed(ctx_encoder);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
synta_byte_array_free(&output);
```
### Example 4: Complete X.509 Certificate Parsing
This example shows a complete side-by-side comparison of parsing X.509 certificates with OpenSSL vs Synta.
For the full working example, see [`synta-ffi/examples/c/openssl_migration_cert.c`](../synta-ffi/examples/c/openssl_migration_cert.c).
#### OpenSSL Version
```c
#include <openssl/x509.h>
#include <openssl/bio.h>
/* Parse DER certificate */
const uint8_t *p = der_data;
X509 *cert = d2i_X509(NULL, &p, (long)der_len);
if (!cert) {
printf("Failed to parse certificate\n");
return;
}
/* Get version */
long version = X509_get_version(cert);
printf("Version: %ld (v%ld)\n", version, version + 1);
/* Get serial number - complex conversion */
const ASN1_INTEGER *serial_asn1 = X509_get_serialNumber(cert);
BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial_asn1, NULL);
char *serial_str = BN_bn2dec(serial_bn);
printf("Serial Number: %s\n", serial_str);
OPENSSL_free(serial_str);
BN_free(serial_bn);
/* Get signature algorithm OID */
const X509_ALGOR *sig_alg = X509_get0_tbs_sigalg(cert);
const ASN1_OBJECT *oid = NULL;
X509_ALGOR_get0(&oid, NULL, NULL, sig_alg);
char oid_str[128];
OBJ_obj2txt(oid_str, sizeof(oid_str), oid, 1);
printf("Signature Algorithm: %s\n", oid_str);
/* Get issuer DN - allocates memory */
X509_NAME *issuer = X509_get_issuer_name(cert);
char *issuer_str = X509_NAME_oneline(issuer, NULL, 0);
printf("Issuer: %s\n", issuer_str);
OPENSSL_free(issuer_str);
/* Get subject DN */
X509_NAME *subject = X509_get_subject_name(cert);
char *subject_str = X509_NAME_oneline(subject, NULL, 0);
printf("Subject: %s\n", subject_str);
OPENSSL_free(subject_str);
/* Must manually free - easy to forget */
X509_free(cert);
```
**Issues with OpenSSL approach:**
- Many helper types (X509_ALGOR, ASN1_OBJECT, BIGNUM, X509_NAME)
- Mix of internal pointers and allocated data
- Must know which pointers to free (X509_free, OPENSSL_free, BN_free)
- No clear indication of ownership
- Complex API for simple tasks
#### Synta Version
```c
#include <synta.h>
static void show_certificate(const uint8_t *der_data, uintptr_t der_len) {
/* Parse DER certificate - returns opaque handle */
SyntaCertificate *cert = synta_certificate_parse_der(der_data, der_len);
if (!cert) {
const char *error = synta_get_last_error_message();
printf("Failed to parse certificate: %s\n", error);
return;
}
/* Get version */
int64_t version;
if (synta_certificate_get_version(cert, &version) == SyntaErrorCode_Success) {
printf("Version: %lld (v%lld)\n", (long long)version, (long long)version + 1);
}
/* Get serial number - returns owned byte array */
SyntaByteArray serial = {0};
if (synta_certificate_get_serial_number(cert, &serial) == SyntaErrorCode_Success) {
printf("Serial Number (hex): ");
for (size_t i = 0; i < serial.len; i++) {
printf("%02x", serial.data[i]);
}
printf("\n");
synta_byte_array_free(&serial);
}
/* Get signature algorithm OID - fills provided buffer */
char oid_str[128];
uintptr_t written = synta_certificate_get_signature_algorithm(cert, oid_str, sizeof(oid_str));
if (written > 0) {
printf("Signature Algorithm: %s\n", oid_str);
}
/* Get issuer DN (raw DER bytes) */
SyntaByteArray issuer = {0};
if (synta_certificate_get_issuer_der(cert, &issuer) == SyntaErrorCode_Success) {
printf("Issuer DN (%u bytes)\n", issuer.len);
synta_byte_array_free(&issuer);
}
/* Get subject DN (raw DER bytes) */
SyntaByteArray subject = {0};
if (synta_certificate_get_subject_der(cert, &subject) == SyntaErrorCode_Success) {
printf("Subject DN (%u bytes)\n", subject.len);
synta_byte_array_free(&subject);
}
/* Single free call - clear ownership */
synta_certificate_free(cert);
}
```
**Advantages of Synta approach:**
- Simple, consistent types (SyntaCertificate, SyntaByteArray)
- Clear ownership (owned data marked with `owned` flag)
- Consistent free functions (synta_certificate_free, synta_byte_array_free)
- Error codes with detailed messages
- No global state - thread-safe by default
- Direct field access without complex conversions
#### Building and Running the Example
```bash
# Build without OpenSSL (Synta only)
cd examples/c
make openssl_migration_cert
# Build with OpenSSL comparison (if OpenSSL is installed)
make openssl_migration_cert_with_openssl
# Run
LD_LIBRARY_PATH=../../target/release ./openssl_migration_cert
```
The complete example shows both implementations side-by-side and highlights the key differences in:
- Memory management complexity
- Error handling approaches
- API complexity and clarity
- Thread safety guarantees
## Benefits
### 1. Memory Safety
OpenSSL requires careful management of allocations and freeing. Synta's Rust backend guarantees memory safety.
**OpenSSL risks:**
- Buffer overflows
- Use-after-free
- Double-free
- Memory leaks
**Synta guarantees:**
- No buffer overflows
- No use-after-free
- No double-free
- Automatic memory management
### 2. Simpler Error Handling
**OpenSSL:**
```c
ASN1_INTEGER *aint = d2i_ASN1_INTEGER(NULL, &p, len);
if (!aint) {
unsigned long err = ERR_get_error();
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
fprintf(stderr, "Error: %s\n", buf);
ERR_clear_error();
}
```
**Synta:**
```c
SyntaInteger *integer = NULL;
SyntaErrorCode err = synta_decode_integer(decoder, &integer);
if (err != SyntaErrorCode_Success) {
fprintf(stderr, "Error: %s\n", synta_error_message(err));
}
```
### 3. No Global State
OpenSSL uses global error queues and state. Synta uses thread-local storage, making it safer for multi-threaded applications.
### 4. Clearer API
Synta's API is designed to be self-documenting:
```c
// Clear ownership and lifetime
SyntaDecoder *decoder = synta_decoder_new(...); // You own it
synta_decoder_free(decoder); // You free it
// Clear what's happening
synta_encoder_start_sequence(encoder, &seq); // Start SEQUENCE
synta_encode_integer_i64(seq, 42); // Add element
synta_encoder_end_constructed(seq); // End SEQUENCE
```
### 5. Better Performance
Synta's zero-copy decoder and efficient encoder can outperform OpenSSL in many scenarios.
## See Also
- [C API Reference](C_API.md)
- [Memory Management Guide](C_MEMORY.md)
- [Migration from libtasn1](MIGRATION_LIBTASN1.md)
- [C Examples](../synta-ffi/examples/c/)